aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDelan Azabani <dazabani@igalia.com>2024-02-27 23:39:06 +0800
committerGitHub <noreply@github.com>2024-02-27 15:39:06 +0000
commitfaf754dfa655f0b9a28f62bc47a78fbf78ebcaf4 (patch)
tree4725e1446680d036797b1fc258733ae6b2c9f354
parentb07505417e629bbb081be9683630f2d7a5f50544 (diff)
downloadservo-faf754dfa655f0b9a28f62bc47a78fbf78ebcaf4.tar.gz
servo-faf754dfa655f0b9a28f62bc47a78fbf78ebcaf4.zip
Move Stylo to its own repo (#31350)
* Remove packages that were moved to external repo * Add workspace dependencies pointing to 2023-06-14 branch * Fix servo-tidy.toml errors * Update commit to include #31346 * Update commit to include servo/stylo#2 * Move css-properties.json lookup to target/doc/stylo * Remove dependency on vendored mako in favour of pypi dependency This also removes etc/ci/generate_workflow.py, which has been unused since at least 9e71bd6a7010d6e5723831696ae0ebe26b47682f. * Add temporary code to debug Windows test failures * Fix failures on Windows due to custom target dir * Update commit to include servo/stylo#3 * Fix license in tests/unit/style/build.rs * Document how to build with local Stylo in Cargo.toml
-rw-r--r--Cargo.lock23
-rw-r--r--Cargo.toml28
-rw-r--r--components/atoms/Cargo.toml17
-rw-r--r--components/atoms/build.rs31
-rw-r--r--components/atoms/lib.rs5
-rw-r--r--components/atoms/static_atoms.txt174
-rw-r--r--components/canvas/Cargo.toml4
-rw-r--r--components/config/Cargo.toml2
-rw-r--r--components/derive_common/Cargo.toml16
-rw-r--r--components/derive_common/cg.rs396
-rw-r--r--components/derive_common/lib.rs13
-rw-r--r--components/derive_common/rustfmt.toml1
-rw-r--r--components/geometry/Cargo.toml2
-rw-r--r--components/gfx/Cargo.toml8
-rw-r--r--components/layout/Cargo.toml10
-rw-r--r--components/layout_2020/Cargo.toml4
-rw-r--r--components/layout_thread/Cargo.toml8
-rw-r--r--components/layout_thread_2020/Cargo.toml8
-rw-r--r--components/malloc_size_of/Cargo.toml52
-rw-r--r--components/malloc_size_of/LICENSE-APACHE201
-rw-r--r--components/malloc_size_of/LICENSE-MIT23
-rw-r--r--components/malloc_size_of/lib.rs978
-rw-r--r--components/malloc_size_of/rustfmt.toml1
-rw-r--r--components/metrics/Cargo.toml2
-rw-r--r--components/net/Cargo.toml4
-rw-r--r--components/pixels/Cargo.toml2
-rw-r--r--components/range/Cargo.toml2
-rw-r--r--components/script/Cargo.toml10
-rw-r--r--components/selectors/Cargo.toml36
-rw-r--r--components/selectors/README.md25
-rw-r--r--components/selectors/attr.rs192
-rw-r--r--components/selectors/bloom.rs422
-rw-r--r--components/selectors/build.rs77
-rw-r--r--components/selectors/builder.rs398
-rw-r--r--components/selectors/context.rs363
-rw-r--r--components/selectors/lib.rs40
-rw-r--r--components/selectors/matching.rs1133
-rw-r--r--components/selectors/nth_index_cache.rs102
-rw-r--r--components/selectors/parser.rs4140
-rw-r--r--components/selectors/rustfmt.toml1
-rw-r--r--components/selectors/sink.rs31
-rw-r--r--components/selectors/tree.rs163
-rw-r--r--components/selectors/visitor.rs111
-rw-r--r--components/servo/Cargo.toml2
-rw-r--r--components/servo_arc/Cargo.toml20
-rw-r--r--components/servo_arc/LICENSE-APACHE201
-rw-r--r--components/servo_arc/LICENSE-MIT23
-rw-r--r--components/servo_arc/lib.rs1370
-rw-r--r--components/servo_arc/rustfmt.toml1
-rw-r--r--components/shared/canvas/Cargo.toml4
-rw-r--r--components/shared/devtools/Cargo.toml2
-rw-r--r--components/shared/gfx/Cargo.toml2
-rw-r--r--components/shared/msg/Cargo.toml4
-rw-r--r--components/shared/net/Cargo.toml4
-rw-r--r--components/shared/script/Cargo.toml4
-rw-r--r--components/shared/script_layout/Cargo.toml10
-rw-r--r--components/size_of_test/Cargo.toml13
-rw-r--r--components/size_of_test/lib.rs14
-rw-r--r--components/style/Cargo.toml95
-rw-r--r--components/style/README.md6
-rw-r--r--components/style/animation.rs1415
-rw-r--r--components/style/applicable_declarations.rs210
-rw-r--r--components/style/attr.rs601
-rw-r--r--components/style/author_styles.rs70
-rw-r--r--components/style/bezier.rs176
-rw-r--r--components/style/bloom.rs401
-rw-r--r--components/style/build.rs87
-rw-r--r--components/style/build_gecko.rs400
-rw-r--r--components/style/color/convert.rs888
-rw-r--r--components/style/color/mix.rs475
-rw-r--r--components/style/color/mod.rs465
-rw-r--r--components/style/context.rs698
-rw-r--r--components/style/counter_style/mod.rs697
-rw-r--r--components/style/counter_style/predefined.rs61
-rwxr-xr-xcomponents/style/counter_style/update_predefined.py35
-rw-r--r--components/style/custom_properties.rs1201
-rw-r--r--components/style/data.rs545
-rw-r--r--components/style/dom.rs940
-rw-r--r--components/style/dom_apis.rs767
-rw-r--r--components/style/driver.rs161
-rw-r--r--components/style/encoding_support.rs105
-rw-r--r--components/style/error_reporting.rs283
-rw-r--r--components/style/font_face.rs835
-rw-r--r--components/style/font_metrics.rs58
-rw-r--r--components/style/gecko/arc_types.rs171
-rw-r--r--components/style/gecko/conversions.rs59
-rw-r--r--components/style/gecko/data.rs198
-rw-r--r--components/style/gecko/media_features.rs1028
-rw-r--r--components/style/gecko/media_queries.rs560
-rw-r--r--components/style/gecko/mod.rs23
-rw-r--r--components/style/gecko/non_ts_pseudo_class_list.rs100
-rw-r--r--components/style/gecko/pseudo_element.rs237
-rw-r--r--components/style/gecko/pseudo_element_definition.mako.rs276
-rwxr-xr-xcomponents/style/gecko/regen_atoms.py218
-rw-r--r--components/style/gecko/restyle_damage.rs121
-rw-r--r--components/style/gecko/selector_parser.rs497
-rw-r--r--components/style/gecko/snapshot.rs174
-rw-r--r--components/style/gecko/snapshot_helpers.rs309
-rw-r--r--components/style/gecko/traversal.rs53
-rw-r--r--components/style/gecko/url.rs383
-rw-r--r--components/style/gecko/values.rs72
-rw-r--r--components/style/gecko/wrapper.rs2125
-rw-r--r--components/style/gecko_bindings/mod.rs28
-rw-r--r--components/style/gecko_bindings/sugar/mod.rs13
-rw-r--r--components/style/gecko_bindings/sugar/ns_com_ptr.rs15
-rw-r--r--components/style/gecko_bindings/sugar/ns_compatibility.rs19
-rw-r--r--components/style/gecko_bindings/sugar/ns_style_auto_array.rs111
-rw-r--r--components/style/gecko_bindings/sugar/ns_t_array.rs144
-rw-r--r--components/style/gecko_bindings/sugar/origin_flags.rs31
-rw-r--r--components/style/gecko_bindings/sugar/ownership.rs61
-rw-r--r--components/style/gecko_bindings/sugar/refptr.rs289
-rw-r--r--components/style/gecko_string_cache/mod.rs532
-rw-r--r--components/style/gecko_string_cache/namespace.rs105
-rw-r--r--components/style/global_style_data.rs173
-rw-r--r--components/style/invalidation/element/document_state.rs142
-rw-r--r--components/style/invalidation/element/element_wrapper.rs391
-rw-r--r--components/style/invalidation/element/invalidation_map.rs547
-rw-r--r--components/style/invalidation/element/invalidator.rs1017
-rw-r--r--components/style/invalidation/element/mod.rs12
-rw-r--r--components/style/invalidation/element/restyle_hints.rs190
-rw-r--r--components/style/invalidation/element/state_and_attributes.rs552
-rw-r--r--components/style/invalidation/media_queries.rs130
-rw-r--r--components/style/invalidation/mod.rs10
-rw-r--r--components/style/invalidation/stylesheets.rs655
-rw-r--r--components/style/invalidation/viewport_units.rs71
-rw-r--r--components/style/lib.rs329
-rw-r--r--components/style/logical_geometry.rs1522
-rw-r--r--components/style/macros.rs98
-rw-r--r--components/style/matching.rs1096
-rw-r--r--components/style/media_queries/media_list.rs150
-rw-r--r--components/style/media_queries/media_query.rs193
-rw-r--r--components/style/media_queries/mod.rs18
-rw-r--r--components/style/parallel.rs197
-rw-r--r--components/style/parser.rs210
-rw-r--r--components/style/piecewise_linear.rs293
-rw-r--r--components/style/properties/Mako-1.1.2-py2.py3-none-any.whlbin75521 -> 0 bytes
-rw-r--r--components/style/properties/build.py172
-rw-r--r--components/style/properties/cascade.rs1258
-rw-r--r--components/style/properties/computed_value_flags.rs190
-rw-r--r--components/style/properties/counted_unknown_properties.py110
-rw-r--r--components/style/properties/data.py912
-rw-r--r--components/style/properties/declaration_block.rs1613
-rw-r--r--components/style/properties/gecko.mako.rs1971
-rw-r--r--components/style/properties/helpers.mako.rs1023
-rw-r--r--components/style/properties/helpers/animated_properties.mako.rs716
-rw-r--r--components/style/properties/longhands/background.mako.rs116
-rw-r--r--components/style/properties/longhands/border.mako.rs159
-rw-r--r--components/style/properties/longhands/box.mako.rs591
-rw-r--r--components/style/properties/longhands/column.mako.rs82
-rw-r--r--components/style/properties/longhands/counters.mako.rs50
-rw-r--r--components/style/properties/longhands/effects.mako.rs86
-rw-r--r--components/style/properties/longhands/font.mako.rs488
-rw-r--r--components/style/properties/longhands/inherited_box.mako.rs96
-rw-r--r--components/style/properties/longhands/inherited_svg.mako.rs217
-rw-r--r--components/style/properties/longhands/inherited_table.mako.rs51
-rw-r--r--components/style/properties/longhands/inherited_text.mako.rs407
-rw-r--r--components/style/properties/longhands/inherited_ui.mako.rs118
-rw-r--r--components/style/properties/longhands/list.mako.rs76
-rw-r--r--components/style/properties/longhands/margin.mako.rs52
-rw-r--r--components/style/properties/longhands/outline.mako.rs51
-rw-r--r--components/style/properties/longhands/padding.mako.rs41
-rw-r--r--components/style/properties/longhands/page.mako.rs42
-rw-r--r--components/style/properties/longhands/position.mako.rs478
-rw-r--r--components/style/properties/longhands/svg.mako.rs258
-rw-r--r--components/style/properties/longhands/table.mako.rs29
-rw-r--r--components/style/properties/longhands/text.mako.rs83
-rw-r--r--components/style/properties/longhands/ui.mako.rs397
-rw-r--r--components/style/properties/longhands/xul.mako.rs79
-rw-r--r--components/style/properties/mod.rs27
-rw-r--r--components/style/properties/properties.html.mako31
-rw-r--r--components/style/properties/properties.mako.rs4277
-rw-r--r--components/style/properties/shorthands/background.mako.rs289
-rw-r--r--components/style/properties/shorthands/border.mako.rs492
-rw-r--r--components/style/properties/shorthands/box.mako.rs311
-rw-r--r--components/style/properties/shorthands/column.mako.rs115
-rw-r--r--components/style/properties/shorthands/font.mako.rs547
-rw-r--r--components/style/properties/shorthands/inherited_svg.mako.rs38
-rw-r--r--components/style/properties/shorthands/inherited_text.mako.rs91
-rw-r--r--components/style/properties/shorthands/list.mako.rs152
-rw-r--r--components/style/properties/shorthands/margin.mako.rs60
-rw-r--r--components/style/properties/shorthands/outline.mako.rs80
-rw-r--r--components/style/properties/shorthands/padding.mako.rs58
-rw-r--r--components/style/properties/shorthands/position.mako.rs893
-rw-r--r--components/style/properties/shorthands/svg.mako.rs258
-rw-r--r--components/style/properties/shorthands/text.mako.rs120
-rw-r--r--components/style/properties/shorthands/ui.mako.rs427
-rw-r--r--components/style/properties_and_values/mod.rs10
-rw-r--r--components/style/properties_and_values/rule.rs245
-rw-r--r--components/style/properties_and_values/syntax/ascii.rs60
-rw-r--r--components/style/properties_and_values/syntax/data_type.rs91
-rw-r--r--components/style/properties_and_values/syntax/mod.rs328
-rw-r--r--components/style/queries/condition.rs366
-rw-r--r--components/style/queries/feature.rs195
-rw-r--r--components/style/queries/feature_expression.rs754
-rw-r--r--components/style/queries/mod.rs19
-rw-r--r--components/style/queries/values.rs36
-rw-r--r--components/style/rule_cache.rs187
-rw-r--r--components/style/rule_collector.rs505
-rw-r--r--components/style/rule_tree/core.rs772
-rw-r--r--components/style/rule_tree/level.rs249
-rw-r--r--components/style/rule_tree/map.rs201
-rw-r--r--components/style/rule_tree/mod.rs426
-rw-r--r--components/style/rule_tree/source.rs75
-rw-r--r--components/style/rule_tree/unsafe_box.rs74
-rw-r--r--components/style/rustfmt.toml1
-rw-r--r--components/style/scoped_tls.rs75
-rw-r--r--components/style/selector_map.rs868
-rw-r--r--components/style/selector_parser.rs240
-rw-r--r--components/style/servo/media_queries.rs305
-rw-r--r--components/style/servo/mod.rs12
-rw-r--r--components/style/servo/restyle_damage.rs269
-rw-r--r--components/style/servo/selector_parser.rs837
-rw-r--r--components/style/servo/url.rs246
-rw-r--r--components/style/shared_lock.rs374
-rw-r--r--components/style/sharing/checks.rs182
-rw-r--r--components/style/sharing/mod.rs910
-rw-r--r--components/style/str.rs189
-rw-r--r--components/style/style_adjuster.rs1013
-rw-r--r--components/style/style_resolver.rs597
-rw-r--r--components/style/stylesheet_set.rs705
-rw-r--r--components/style/stylesheets/container_rule.rs632
-rw-r--r--components/style/stylesheets/counter_style_rule.rs7
-rw-r--r--components/style/stylesheets/document_rule.rs305
-rw-r--r--components/style/stylesheets/font_feature_values_rule.rs490
-rw-r--r--components/style/stylesheets/font_palette_values_rule.rs268
-rw-r--r--components/style/stylesheets/import_rule.rs332
-rw-r--r--components/style/stylesheets/keyframes_rule.rs691
-rw-r--r--components/style/stylesheets/layer_rule.rs228
-rw-r--r--components/style/stylesheets/loader.rs31
-rw-r--r--components/style/stylesheets/media_rule.rs71
-rw-r--r--components/style/stylesheets/mod.rs582
-rw-r--r--components/style/stylesheets/namespace_rule.rs43
-rw-r--r--components/style/stylesheets/origin.rs247
-rw-r--r--components/style/stylesheets/page_rule.rs145
-rw-r--r--components/style/stylesheets/property_rule.rs5
-rw-r--r--components/style/stylesheets/rule_list.rs198
-rw-r--r--components/style/stylesheets/rule_parser.rs867
-rw-r--r--components/style/stylesheets/rules_iterator.rs326
-rw-r--r--components/style/stylesheets/style_rule.rs104
-rw-r--r--components/style/stylesheets/stylesheet.rs605
-rw-r--r--components/style/stylesheets/supports_rule.rs448
-rw-r--r--components/style/stylist.rs3290
-rw-r--r--components/style/thread_state.rs97
-rw-r--r--components/style/traversal.rs841
-rw-r--r--components/style/traversal_flags.rs67
-rw-r--r--components/style/use_counters/mod.rs96
-rw-r--r--components/style/values/animated/color.rs88
-rw-r--r--components/style/values/animated/effects.rs27
-rw-r--r--components/style/values/animated/font.rs37
-rw-r--r--components/style/values/animated/grid.rs157
-rw-r--r--components/style/values/animated/lists.rs141
-rw-r--r--components/style/values/animated/mod.rs488
-rw-r--r--components/style/values/animated/svg.rs46
-rw-r--r--components/style/values/animated/transform.rs1473
-rw-r--r--components/style/values/computed/align.rs91
-rw-r--r--components/style/values/computed/angle.rs101
-rw-r--r--components/style/values/computed/animation.rs69
-rw-r--r--components/style/values/computed/background.rs13
-rw-r--r--components/style/values/computed/basic_shape.rs42
-rw-r--r--components/style/values/computed/border.rs84
-rw-r--r--components/style/values/computed/box.rs268
-rw-r--r--components/style/values/computed/color.rs104
-rw-r--r--components/style/values/computed/column.rs11
-rw-r--r--components/style/values/computed/counters.rs26
-rw-r--r--components/style/values/computed/easing.rs109
-rw-r--r--components/style/values/computed/effects.rs44
-rw-r--r--components/style/values/computed/flex.rs19
-rw-r--r--components/style/values/computed/font.rs1281
-rw-r--r--components/style/values/computed/image.rs213
-rw-r--r--components/style/values/computed/length.rs522
-rw-r--r--components/style/values/computed/length_percentage.rs899
-rw-r--r--components/style/values/computed/list.rs17
-rw-r--r--components/style/values/computed/mod.rs998
-rw-r--r--components/style/values/computed/motion.rs65
-rw-r--r--components/style/values/computed/outline.rs7
-rw-r--r--components/style/values/computed/page.rs75
-rw-r--r--components/style/values/computed/percentage.rs136
-rw-r--r--components/style/values/computed/position.rs74
-rw-r--r--components/style/values/computed/ratio.rs115
-rw-r--r--components/style/values/computed/rect.rs11
-rw-r--r--components/style/values/computed/resolution.rs56
-rw-r--r--components/style/values/computed/svg.rs68
-rw-r--r--components/style/values/computed/table.rs7
-rw-r--r--components/style/values/computed/text.rs262
-rw-r--r--components/style/values/computed/time.rs45
-rw-r--r--components/style/values/computed/transform.rs558
-rw-r--r--components/style/values/computed/ui.rs22
-rw-r--r--components/style/values/computed/url.rs15
-rw-r--r--components/style/values/distance.rs138
-rw-r--r--components/style/values/generics/animation.rs140
-rw-r--r--components/style/values/generics/background.rs54
-rw-r--r--components/style/values/generics/basic_shape.rs513
-rw-r--r--components/style/values/generics/border.rs257
-rw-r--r--components/style/values/generics/box.rs208
-rw-r--r--components/style/values/generics/calc.rs1343
-rw-r--r--components/style/values/generics/color.rs196
-rw-r--r--components/style/values/generics/column.rs45
-rw-r--r--components/style/values/generics/counters.rs287
-rw-r--r--components/style/values/generics/easing.rs135
-rw-r--r--components/style/values/generics/effects.rs121
-rw-r--r--components/style/values/generics/flex.rs33
-rw-r--r--components/style/values/generics/font.rs271
-rw-r--r--components/style/values/generics/grid.rs829
-rw-r--r--components/style/values/generics/image.rs614
-rw-r--r--components/style/values/generics/length.rs314
-rw-r--r--components/style/values/generics/mod.rs386
-rw-r--r--components/style/values/generics/motion.rs205
-rw-r--r--components/style/values/generics/page.rs162
-rw-r--r--components/style/values/generics/position.rs237
-rw-r--r--components/style/values/generics/ratio.rs50
-rw-r--r--components/style/values/generics/rect.rs126
-rw-r--r--components/style/values/generics/size.rs99
-rw-r--r--components/style/values/generics/svg.rs221
-rw-r--r--components/style/values/generics/text.rs156
-rw-r--r--components/style/values/generics/transform.rs886
-rw-r--r--components/style/values/generics/ui.rs129
-rw-r--r--components/style/values/generics/url.rs47
-rw-r--r--components/style/values/mod.rs793
-rw-r--r--components/style/values/resolved/color.rs48
-rw-r--r--components/style/values/resolved/counters.rs51
-rw-r--r--components/style/values/resolved/mod.rs277
-rw-r--r--components/style/values/specified/align.rs817
-rw-r--r--components/style/values/specified/angle.rs276
-rw-r--r--components/style/values/specified/animation.rs420
-rw-r--r--components/style/values/specified/background.rs143
-rw-r--r--components/style/values/specified/basic_shape.rs321
-rw-r--r--components/style/values/specified/border.rs398
-rw-r--r--components/style/values/specified/box.rs1879
-rw-r--r--components/style/values/specified/calc.rs1047
-rw-r--r--components/style/values/specified/color.rs1184
-rw-r--r--components/style/values/specified/column.rs11
-rw-r--r--components/style/values/specified/counters.rs281
-rw-r--r--components/style/values/specified/easing.rs252
-rw-r--r--components/style/values/specified/effects.rs450
-rw-r--r--components/style/values/specified/flex.rs25
-rw-r--r--components/style/values/specified/font.rs2125
-rw-r--r--components/style/values/specified/gecko.rs82
-rw-r--r--components/style/values/specified/grid.rs349
-rw-r--r--components/style/values/specified/image.rs1308
-rw-r--r--components/style/values/specified/length.rs1849
-rw-r--r--components/style/values/specified/list.rs202
-rw-r--r--components/style/values/specified/mod.rs953
-rw-r--r--components/style/values/specified/motion.rs238
-rw-r--r--components/style/values/specified/outline.rs71
-rw-r--r--components/style/values/specified/page.rs99
-rw-r--r--components/style/values/specified/percentage.rs225
-rw-r--r--components/style/values/specified/position.rs905
-rw-r--r--components/style/values/specified/ratio.rs32
-rw-r--r--components/style/values/specified/rect.rs11
-rw-r--r--components/style/values/specified/resolution.rs141
-rw-r--r--components/style/values/specified/source_size_list.rs136
-rw-r--r--components/style/values/specified/svg.rs391
-rw-r--r--components/style/values/specified/svg_path.rs1030
-rw-r--r--components/style/values/specified/table.rs36
-rw-r--r--components/style/values/specified/text.rs1154
-rw-r--r--components/style/values/specified/time.rs174
-rw-r--r--components/style/values/specified/transform.rs487
-rw-r--r--components/style/values/specified/ui.rs232
-rw-r--r--components/style/values/specified/url.rs15
-rw-r--r--components/style_config/Cargo.toml14
-rw-r--r--components/style_config/lib.rs97
-rw-r--r--components/style_derive/Cargo.toml18
-rw-r--r--components/style_derive/animate.rs135
-rw-r--r--components/style_derive/compute_squared_distance.rs125
-rw-r--r--components/style_derive/lib.rs82
-rw-r--r--components/style_derive/parse.rs323
-rw-r--r--components/style_derive/rustfmt.toml1
-rw-r--r--components/style_derive/specified_value_info.rs195
-rw-r--r--components/style_derive/to_animated_value.rs35
-rw-r--r--components/style_derive/to_animated_zero.rs65
-rw-r--r--components/style_derive/to_computed_value.rs205
-rw-r--r--components/style_derive/to_css.rs396
-rw-r--r--components/style_derive/to_resolved_value.rs52
-rw-r--r--components/style_static_prefs/Cargo.toml7
-rw-r--r--components/style_static_prefs/src/lib.rs162
-rw-r--r--components/style_traits/Cargo.toml32
-rw-r--r--components/style_traits/arc_slice.rs163
-rw-r--r--components/style_traits/dom.rs216
-rw-r--r--components/style_traits/lib.rs281
-rw-r--r--components/style_traits/owned_slice.rs198
-rw-r--r--components/style_traits/owned_str.rs81
-rw-r--r--components/style_traits/rustfmt.toml1
-rw-r--r--components/style_traits/specified_value_info.rs138
-rw-r--r--components/style_traits/values.rs567
-rw-r--r--components/to_shmem/Cargo.toml22
-rw-r--r--components/to_shmem/lib.rs596
-rw-r--r--components/to_shmem/rustfmt.toml1
-rw-r--r--components/to_shmem_derive/Cargo.toml18
-rw-r--r--components/to_shmem_derive/lib.rs26
-rw-r--r--components/to_shmem_derive/to_shmem.rs78
-rw-r--r--components/url/Cargo.toml6
-rw-r--r--components/webgpu/Cargo.toml2
-rw-r--r--etc/ci/generate_workflow.py58
-rw-r--r--python/requirements.txt3
-rw-r--r--python/servo/package_commands.py4
-rw-r--r--servo-tidy.toml7
-rw-r--r--tests/unit/malloc_size_of/Cargo.toml4
-rw-r--r--tests/unit/style/Cargo.toml10
-rw-r--r--tests/unit/style/build.rs (renamed from components/style/stylesheets/font_face_rule.rs)7
-rw-r--r--tests/unit/style/properties/scaffolding.rs11
400 files changed, 112 insertions, 123600 deletions
diff --git a/Cargo.lock b/Cargo.lock
index 71adf0ab738..f336e1928eb 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -1208,6 +1208,7 @@ dependencies = [
[[package]]
name = "derive_common"
version = "0.0.1"
+source = "git+https://github.com/servo/stylo.git?branch=2023-06-14#9d04df286684bbe1b05858ed1b337b006784bf36"
dependencies = [
"darling",
"proc-macro2",
@@ -3459,6 +3460,7 @@ dependencies = [
[[package]]
name = "malloc_size_of"
version = "0.0.1"
+source = "git+https://github.com/servo/stylo.git?branch=2023-06-14#9d04df286684bbe1b05858ed1b337b006784bf36"
dependencies = [
"accountable-refcell",
"app_units",
@@ -3701,12 +3703,6 @@ dependencies = [
]
[[package]]
-name = "mozbuild"
-version = "0.1.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "903970ae2f248d7275214cf8f387f8ba0c4ea7e3d87a320e85493db60ce28616"
-
-[[package]]
name = "mozjs"
version = "0.14.1"
source = "git+https://github.com/servo/mozjs#d1f1519b8a9a57d97e3f623ea119e86c5dcbf524"
@@ -5076,6 +5072,7 @@ dependencies = [
[[package]]
name = "selectors"
version = "0.24.0"
+source = "git+https://github.com/servo/stylo.git?branch=2023-06-14#9d04df286684bbe1b05858ed1b337b006784bf36"
dependencies = [
"bitflags 1.3.2",
"cssparser",
@@ -5363,6 +5360,7 @@ dependencies = [
[[package]]
name = "servo_arc"
version = "0.2.0"
+source = "git+https://github.com/servo/stylo.git?branch=2023-06-14#9d04df286684bbe1b05858ed1b337b006784bf36"
dependencies = [
"nodrop",
"serde",
@@ -5372,6 +5370,7 @@ dependencies = [
[[package]]
name = "servo_atoms"
version = "0.0.1"
+source = "git+https://github.com/servo/stylo.git?branch=2023-06-14#9d04df286684bbe1b05858ed1b337b006784bf36"
dependencies = [
"string_cache",
"string_cache_codegen",
@@ -5576,6 +5575,7 @@ checksum = "38b58827f4464d87d377d175e90bf58eb00fd8716ff0a62f80356b5e61555d0d"
[[package]]
name = "size_of_test"
version = "0.0.1"
+source = "git+https://github.com/servo/stylo.git?branch=2023-06-14#9d04df286684bbe1b05858ed1b337b006784bf36"
dependencies = [
"static_assertions",
]
@@ -5701,6 +5701,7 @@ checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f"
[[package]]
name = "static_prefs"
version = "0.1.0"
+source = "git+https://github.com/servo/stylo.git?branch=2023-06-14#9d04df286684bbe1b05858ed1b337b006784bf36"
[[package]]
name = "str-buf"
@@ -5743,11 +5744,11 @@ dependencies = [
[[package]]
name = "style"
version = "0.0.1"
+source = "git+https://github.com/servo/stylo.git?branch=2023-06-14#9d04df286684bbe1b05858ed1b337b006784bf36"
dependencies = [
"app_units",
"arrayvec",
"atomic_refcell",
- "bindgen",
"bitflags 1.3.2",
"byteorder",
"cssparser",
@@ -5764,7 +5765,6 @@ dependencies = [
"malloc_size_of",
"malloc_size_of_derive",
"mime",
- "mozbuild",
"new_debug_unreachable",
"num-derive",
"num-integer",
@@ -5774,7 +5774,6 @@ dependencies = [
"parking_lot",
"precomputed-hash",
"rayon",
- "regex",
"selectors",
"serde",
"servo_arc",
@@ -5791,7 +5790,6 @@ dependencies = [
"time 0.1.45",
"to_shmem",
"to_shmem_derive",
- "toml 0.5.11",
"uluru",
"unicode-bidi",
"unicode-segmentation",
@@ -5803,6 +5801,7 @@ dependencies = [
[[package]]
name = "style_config"
version = "0.0.1"
+source = "git+https://github.com/servo/stylo.git?branch=2023-06-14#9d04df286684bbe1b05858ed1b337b006784bf36"
dependencies = [
"lazy_static",
]
@@ -5810,6 +5809,7 @@ dependencies = [
[[package]]
name = "style_derive"
version = "0.0.1"
+source = "git+https://github.com/servo/stylo.git?branch=2023-06-14#9d04df286684bbe1b05858ed1b337b006784bf36"
dependencies = [
"darling",
"derive_common",
@@ -5840,6 +5840,7 @@ dependencies = [
[[package]]
name = "style_traits"
version = "0.0.1"
+source = "git+https://github.com/servo/stylo.git?branch=2023-06-14#9d04df286684bbe1b05858ed1b337b006784bf36"
dependencies = [
"app_units",
"bitflags 1.3.2",
@@ -6182,6 +6183,7 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20"
[[package]]
name = "to_shmem"
version = "0.0.1"
+source = "git+https://github.com/servo/stylo.git?branch=2023-06-14#9d04df286684bbe1b05858ed1b337b006784bf36"
dependencies = [
"cssparser",
"servo_arc",
@@ -6194,6 +6196,7 @@ dependencies = [
[[package]]
name = "to_shmem_derive"
version = "0.0.1"
+source = "git+https://github.com/servo/stylo.git?branch=2023-06-14#9d04df286684bbe1b05858ed1b337b006784bf36"
dependencies = [
"darling",
"derive_common",
diff --git a/Cargo.toml b/Cargo.toml
index 00b606a941d..95f2f9622a2 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -65,6 +65,7 @@ layout_traits = { path = "components/shared/layout" }
lazy_static = "1.4"
libc = "0.2"
log = "0.4"
+malloc_size_of = { git = "https://github.com/servo/stylo.git", branch = "2023-06-14", features = ["servo"] }
malloc_size_of_derive = "0.1"
mime = "0.3.13"
mime_guess = "2.0.3"
@@ -87,22 +88,28 @@ rustls = { version = "0.21.10", features = ["dangerous_configuration"] }
rustls-pemfile = "1.0.4"
script_layout_interface = { path = "components/shared/script_layout" }
script_traits = { path = "components/shared/script" }
+selectors = { git = "https://github.com/servo/stylo.git", branch = "2023-06-14" }
serde = "1.0.197"
serde_bytes = "0.11"
serde_json = "1.0"
+servo_arc = { git = "https://github.com/servo/stylo.git", branch = "2023-06-14" }
+servo_atoms = { git = "https://github.com/servo/stylo.git", branch = "2023-06-14" }
+size_of_test = { git = "https://github.com/servo/stylo.git", branch = "2023-06-14" }
smallbitvec = "2.3.0"
smallvec = "1.13"
sparkle = "0.1.26"
string_cache = "0.8"
string_cache_codegen = "0.5"
-style_config = { path = "components/style_config" }
-style_traits = { path = "components/style_traits", features = ["servo"] }
+style = { git = "https://github.com/servo/stylo.git", branch = "2023-06-14", features = ["servo"] }
+style_config = { git = "https://github.com/servo/stylo.git", branch = "2023-06-14" }
+style_traits = { git = "https://github.com/servo/stylo.git", branch = "2023-06-14", features = ["servo"] }
# NOTE: the sm-angle feature only enables ANGLE on Windows, not other platforms!
surfman = { version = "0.9", features = ["chains", "sm-angle", "sm-angle-default"] }
syn = { version = "2", default-features = false, features = ["clone-impls", "derive", "parsing"] }
synstructure = "0.13"
thin-vec = "0.2.13"
time = "0.1.41"
+to_shmem = { git = "https://github.com/servo/stylo.git", branch = "2023-06-14" }
tokio = "1"
tokio-rustls = "0.24"
tungstenite = "0.20"
@@ -140,7 +147,22 @@ debug-assertions = false
#
# <crate> = { path = "/path/to/local/checkout" }
#
-# Or for a git dependency:
+# Or for Stylo:
+#
+# [patch."https://github.com/servo/stylo.git"]
+# derive_common = { path = "../stylo/derive_common" }
+# malloc_size_of = { path = "../stylo/malloc_size_of" }
+# selectors = { path = "../stylo/selectors" }
+# servo_arc = { path = "../stylo/servo_arc" }
+# servo_atoms = { path = "../stylo/atoms" }
+# size_of_test = { path = "../stylo/size_of_test" }
+# static_prefs = { path = "../stylo/style_static_prefs" }
+# style_config = { path = "../stylo/style_config" }
+# style_derive = { path = "../stylo/style_derive" }
+# style = { path = "../stylo/style" }
+# style_traits = { path = "../stylo/style_traits" }
+#
+# Or for another git dependency:
#
# [patch."https://github.com/servo/<repository>"]
# <crate> = { path = "/path/to/local/checkout" }
diff --git a/components/atoms/Cargo.toml b/components/atoms/Cargo.toml
deleted file mode 100644
index d48dbf0a350..00000000000
--- a/components/atoms/Cargo.toml
+++ /dev/null
@@ -1,17 +0,0 @@
-[package]
-name = "servo_atoms"
-version = "0.0.1"
-authors = ["The Servo Project Developers"]
-license = "MPL-2.0"
-edition = "2018"
-publish = false
-build = "build.rs"
-
-[lib]
-path = "lib.rs"
-
-[dependencies]
-string_cache = { workspace = true }
-
-[build-dependencies]
-string_cache_codegen = { workspace = true }
diff --git a/components/atoms/build.rs b/components/atoms/build.rs
deleted file mode 100644
index 6bd2de37034..00000000000
--- a/components/atoms/build.rs
+++ /dev/null
@@ -1,31 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
-
-use std::env;
-use std::fs::File;
-use std::io::{BufRead, BufReader};
-use std::path::Path;
-
-fn main() {
- let static_atoms =
- Path::new(&env::var_os("CARGO_MANIFEST_DIR").unwrap()).join("static_atoms.txt");
- let static_atoms = BufReader::new(File::open(&static_atoms).unwrap());
- let mut atom_type = string_cache_codegen::AtomType::new("Atom", "atom!");
-
- macro_rules! predefined {
- ($($name: expr,)+) => {
- {
- $(
- atom_type.atom($name);
- )+
- }
- }
- }
- include!("../style/counter_style/predefined.rs");
-
- atom_type
- .atoms(static_atoms.lines().map(Result::unwrap))
- .write_to_file(&Path::new(&env::var_os("OUT_DIR").unwrap()).join("atom.rs"))
- .unwrap();
-}
diff --git a/components/atoms/lib.rs b/components/atoms/lib.rs
deleted file mode 100644
index 03560a40c0b..00000000000
--- a/components/atoms/lib.rs
+++ /dev/null
@@ -1,5 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
-
-include!(concat!(env!("OUT_DIR"), "/atom.rs"));
diff --git a/components/atoms/static_atoms.txt b/components/atoms/static_atoms.txt
deleted file mode 100644
index 5d8f7754270..00000000000
--- a/components/atoms/static_atoms.txt
+++ /dev/null
@@ -1,174 +0,0 @@
--moz-content-preferred-color-scheme
--moz-device-pixel-ratio
--moz-gtk-csd-close-button-position
--moz-gtk-csd-maximize-button-position
--moz-gtk-csd-menu-radius
--moz-gtk-csd-minimize-button-position
--moz-gtk-csd-titlebar-radius
--moz-gtk-menu-radius
-DOMContentLoaded
-abort
-activate
-addtrack
-animationcancel
-animationend
-animationiteration
-animationstart
-aspect-ratio
-beforeunload
-block-size
-button
-canplay
-canplaythrough
-center
-change
-characteristicvaluechanged
-checkbox
-click
-close
-closing
-color
-complete
-compositionend
-compositionstart
-compositionupdate
-controllerchange
-cursive
-dark
-datachannel
-date
-datetime-local
-dir
-device-pixel-ratio
-durationchange
-email
-emptied
-end
-ended
-error
-fantasy
-fetch
-file
-fill
-fill-opacity
-formdata
-fullscreenchange
-fullscreenerror
-gattserverdisconnected
-hashchange
-height
-hidden
-icecandidate
-iceconnectionstatechange
-icegatheringstatechange
-image
-inline-size
-input
-inputsourceschange
-invalid
-keydown
-keypress
-kind
-left
-light
-ltr
-load
-loadeddata
-loadedmetadata
-loadend
-loadstart
-message
-message
-messageerror
-monospace
-month
-mousedown
-mousemove
-mouseover
-mouseup
-negotiationneeded
-none
-normal
-number
-onchange
-open
-orientation
-pagehide
-pageshow
-password
-pause
-play
-playing
-popstate
-postershown
-print
-progress
-radio
-range
-ratechange
-readystatechange
-referrer
-reftest-wait
-rejectionhandled
-removetrack
-reset
-resize
-resolution
-resourcetimingbufferfull
-right
-rtl
-sans-serif
-safe-area-inset-top
-safe-area-inset-bottom
-safe-area-inset-left
-safe-area-inset-right
-scan
-screen
-scroll-position
-scrollbar-inline-size
-search
-seeked
-seeking
-select
-selectend
-selectionchange
-selectstart
-serif
-sessionavailable
-signalingstatechange
-squeeze
-squeezeend
-squeezestart
-srclang
-statechange
-stroke
-stroke-opacity
-storage
-submit
-suspend
-system-ui
-tel
-text
-time
-timeupdate
-toggle
-track
-transitioncancel
-transitionend
-transitionrun
-transitionstart
-uncapturederror
-unhandledrejection
-unload
-url
-visibilitychange
-volumechange
-waiting
-webglcontextcreationerror
-webkitAnimationEnd
-webkitAnimationIteration
-webkitAnimationStart
-webkitTransitionEnd
-webkitTransitionRun
-week
-width
diff --git a/components/canvas/Cargo.toml b/components/canvas/Cargo.toml
index 692d2efdfce..ce420ae0c01 100644
--- a/components/canvas/Cargo.toml
+++ b/components/canvas/Cargo.toml
@@ -33,9 +33,9 @@ num-traits = { workspace = true }
pathfinder_geometry = "0.5"
pixels = { path = "../pixels" }
raqote = "0.8.2"
-servo_arc = { path = "../servo_arc" }
+servo_arc = { workspace = true }
sparkle = { workspace = true }
-style = { path = "../style" }
+style = { workspace = true }
style_traits = { workspace = true }
surfman = { workspace = true }
time = { workspace = true, optional = true }
diff --git a/components/config/Cargo.toml b/components/config/Cargo.toml
index 8befa01cb54..ea92a1d99f8 100644
--- a/components/config/Cargo.toml
+++ b/components/config/Cargo.toml
@@ -22,7 +22,7 @@ serde_json = { workspace = true }
servo_config_plugins = { path = "../config_plugins" }
servo_geometry = { path = "../geometry" }
servo_url = { path = "../url" }
-style_config = { path = "../style_config" }
+style_config = { workspace = true }
url = { workspace = true }
[target.'cfg(not(target_os = "android"))'.dependencies]
diff --git a/components/derive_common/Cargo.toml b/components/derive_common/Cargo.toml
deleted file mode 100644
index 5677069ad56..00000000000
--- a/components/derive_common/Cargo.toml
+++ /dev/null
@@ -1,16 +0,0 @@
-[package]
-name = "derive_common"
-version = "0.0.1"
-authors = ["The Servo Project Developers"]
-license = "MPL-2.0"
-publish = false
-
-[lib]
-path = "lib.rs"
-
-[dependencies]
-darling = { workspace = true }
-proc-macro2 = { workspace = true }
-quote = { workspace = true }
-syn = { workspace = true }
-synstructure = { workspace = true }
diff --git a/components/derive_common/cg.rs b/components/derive_common/cg.rs
deleted file mode 100644
index 73301af2ff2..00000000000
--- a/components/derive_common/cg.rs
+++ /dev/null
@@ -1,396 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
-
-use darling::{FromDeriveInput, FromField, FromVariant};
-use proc_macro2::{Span, TokenStream};
-use quote::TokenStreamExt;
-use syn::{self, AngleBracketedGenericArguments, AssocType, DeriveInput, Field};
-use syn::{GenericArgument, GenericParam, Ident, Path};
-use syn::{PathArguments, PathSegment, QSelf, Type, TypeArray, TypeGroup};
-use syn::{TypeParam, TypeParen, TypePath, TypeSlice, TypeTuple};
-use syn::{Variant, WherePredicate};
-use synstructure::{self, BindStyle, BindingInfo, VariantAst, VariantInfo};
-
-/// Given an input type which has some where clauses already, like:
-///
-/// struct InputType<T>
-/// where
-/// T: Zero,
-/// {
-/// ...
-/// }
-///
-/// Add the necessary `where` clauses so that the output type of a trait
-/// fulfils them.
-///
-/// For example:
-///
-/// ```ignore
-/// <T as ToComputedValue>::ComputedValue: Zero,
-/// ```
-///
-/// This needs to run before adding other bounds to the type parameters.
-pub fn propagate_clauses_to_output_type(
- where_clause: &mut Option<syn::WhereClause>,
- generics: &syn::Generics,
- trait_path: &Path,
- trait_output: &Ident,
-) {
- let where_clause = match *where_clause {
- Some(ref mut clause) => clause,
- None => return,
- };
- let mut extra_bounds = vec![];
- for pred in &where_clause.predicates {
- let ty = match *pred {
- syn::WherePredicate::Type(ref ty) => ty,
- ref predicate => panic!("Unhanded complex where predicate: {:?}", predicate),
- };
-
- let path = match ty.bounded_ty {
- syn::Type::Path(ref p) => &p.path,
- ref ty => panic!("Unhanded complex where type: {:?}", ty),
- };
-
- assert!(
- ty.lifetimes.is_none(),
- "Unhanded complex lifetime bound: {:?}",
- ty,
- );
-
- let ident = match path_to_ident(path) {
- Some(i) => i,
- None => panic!("Unhanded complex where type path: {:?}", path),
- };
-
- if generics.type_params().any(|param| param.ident == *ident) {
- extra_bounds.push(ty.clone());
- }
- }
-
- for bound in extra_bounds {
- let ty = bound.bounded_ty;
- let bounds = bound.bounds;
- where_clause
- .predicates
- .push(parse_quote!(<#ty as #trait_path>::#trait_output: #bounds))
- }
-}
-
-pub fn add_predicate(where_clause: &mut Option<syn::WhereClause>, pred: WherePredicate) {
- where_clause
- .get_or_insert(parse_quote!(where))
- .predicates
- .push(pred);
-}
-
-pub fn fmap_match<F>(input: &DeriveInput, bind_style: BindStyle, f: F) -> TokenStream
-where
- F: FnMut(&BindingInfo) -> TokenStream,
-{
- fmap2_match(input, bind_style, f, |_| None)
-}
-
-pub fn fmap2_match<F, G>(
- input: &DeriveInput,
- bind_style: BindStyle,
- mut f: F,
- mut g: G,
-) -> TokenStream
-where
- F: FnMut(&BindingInfo) -> TokenStream,
- G: FnMut(&BindingInfo) -> Option<TokenStream>,
-{
- let mut s = synstructure::Structure::new(input);
- s.variants_mut().iter_mut().for_each(|v| {
- v.bind_with(|_| bind_style);
- });
- s.each_variant(|variant| {
- let (mapped, mapped_fields) = value(variant, "mapped");
- let fields_pairs = variant.bindings().iter().zip(mapped_fields.iter());
- let mut computations = quote!();
- computations.append_all(fields_pairs.map(|(field, mapped_field)| {
- let expr = f(field);
- quote! { let #mapped_field = #expr; }
- }));
- computations.append_all(
- mapped_fields
- .iter()
- .map(|mapped_field| match g(mapped_field) {
- Some(expr) => quote! { let #mapped_field = #expr; },
- None => quote!(),
- }),
- );
- computations.append_all(mapped);
- Some(computations)
- })
-}
-
-pub fn fmap_trait_output(input: &DeriveInput, trait_path: &Path, trait_output: &Ident) -> Path {
- let segment = PathSegment {
- ident: input.ident.clone(),
- arguments: PathArguments::AngleBracketed(AngleBracketedGenericArguments {
- args: input
- .generics
- .params
- .iter()
- .map(|arg| match arg {
- &GenericParam::Lifetime(ref data) => {
- GenericArgument::Lifetime(data.lifetime.clone())
- },
- &GenericParam::Type(ref data) => {
- let ident = &data.ident;
- GenericArgument::Type(parse_quote!(<#ident as #trait_path>::#trait_output))
- },
- &GenericParam::Const(ref inner) => {
- let ident = &inner.ident;
- GenericArgument::Const(parse_quote!(#ident))
- },
- })
- .collect(),
- colon2_token: Default::default(),
- gt_token: Default::default(),
- lt_token: Default::default(),
- }),
- };
- segment.into()
-}
-
-pub fn map_type_params<F>(ty: &Type, params: &[&TypeParam], self_type: &Path, f: &mut F) -> Type
-where
- F: FnMut(&Ident) -> Type,
-{
- match *ty {
- Type::Slice(ref inner) => Type::from(TypeSlice {
- elem: Box::new(map_type_params(&inner.elem, params, self_type, f)),
- ..inner.clone()
- }),
- Type::Array(ref inner) => {
- //ref ty, ref expr) => {
- Type::from(TypeArray {
- elem: Box::new(map_type_params(&inner.elem, params, self_type, f)),
- ..inner.clone()
- })
- },
- ref ty @ Type::Never(_) => ty.clone(),
- Type::Tuple(ref inner) => Type::from(TypeTuple {
- elems: inner
- .elems
- .iter()
- .map(|ty| map_type_params(&ty, params, self_type, f))
- .collect(),
- ..inner.clone()
- }),
- Type::Path(TypePath {
- qself: None,
- ref path,
- }) => {
- if let Some(ident) = path_to_ident(path) {
- if params.iter().any(|ref param| &param.ident == ident) {
- return f(ident);
- }
- if ident == "Self" {
- return Type::from(TypePath {
- qself: None,
- path: self_type.clone(),
- });
- }
- }
- Type::from(TypePath {
- qself: None,
- path: map_type_params_in_path(path, params, self_type, f),
- })
- },
- Type::Path(TypePath {
- ref qself,
- ref path,
- }) => Type::from(TypePath {
- qself: qself.as_ref().map(|qself| QSelf {
- ty: Box::new(map_type_params(&qself.ty, params, self_type, f)),
- position: qself.position,
- ..qself.clone()
- }),
- path: map_type_params_in_path(path, params, self_type, f),
- }),
- Type::Paren(ref inner) => Type::from(TypeParen {
- elem: Box::new(map_type_params(&inner.elem, params, self_type, f)),
- ..inner.clone()
- }),
- Type::Group(ref inner) => Type::from(TypeGroup {
- elem: Box::new(map_type_params(&inner.elem, params, self_type, f)),
- ..inner.clone()
- }),
- ref ty => panic!("type {:?} cannot be mapped yet", ty),
- }
-}
-
-fn map_type_params_in_path<F>(
- path: &Path,
- params: &[&TypeParam],
- self_type: &Path,
- f: &mut F,
-) -> Path
-where
- F: FnMut(&Ident) -> Type,
-{
- Path {
- leading_colon: path.leading_colon,
- segments: path
- .segments
- .iter()
- .map(|segment| PathSegment {
- ident: segment.ident.clone(),
- arguments: match segment.arguments {
- PathArguments::AngleBracketed(ref data) => {
- PathArguments::AngleBracketed(AngleBracketedGenericArguments {
- args: data
- .args
- .iter()
- .map(|arg| match arg {
- ty @ &GenericArgument::Lifetime(_) => ty.clone(),
- &GenericArgument::Type(ref data) => GenericArgument::Type(
- map_type_params(data, params, self_type, f),
- ),
- &GenericArgument::AssocType(ref data) => {
- GenericArgument::AssocType(AssocType {
- ty: map_type_params(&data.ty, params, self_type, f),
- ..data.clone()
- })
- },
- ref arg => panic!("arguments {:?} cannot be mapped yet", arg),
- })
- .collect(),
- ..data.clone()
- })
- },
- ref arg @ PathArguments::None => arg.clone(),
- ref parameters => panic!("parameters {:?} cannot be mapped yet", parameters),
- },
- })
- .collect(),
- }
-}
-
-fn path_to_ident(path: &Path) -> Option<&Ident> {
- match *path {
- Path {
- leading_colon: None,
- ref segments,
- } if segments.len() == 1 => {
- if segments[0].arguments.is_empty() {
- Some(&segments[0].ident)
- } else {
- None
- }
- },
- _ => None,
- }
-}
-
-pub fn parse_field_attrs<A>(field: &Field) -> A
-where
- A: FromField,
-{
- match A::from_field(field) {
- Ok(attrs) => attrs,
- Err(e) => panic!("failed to parse field attributes: {}", e),
- }
-}
-
-pub fn parse_input_attrs<A>(input: &DeriveInput) -> A
-where
- A: FromDeriveInput,
-{
- match A::from_derive_input(input) {
- Ok(attrs) => attrs,
- Err(e) => panic!("failed to parse input attributes: {}", e),
- }
-}
-
-pub fn parse_variant_attrs_from_ast<A>(variant: &VariantAst) -> A
-where
- A: FromVariant,
-{
- let v = Variant {
- ident: variant.ident.clone(),
- attrs: variant.attrs.to_vec(),
- fields: variant.fields.clone(),
- discriminant: variant.discriminant.clone(),
- };
- parse_variant_attrs(&v)
-}
-
-pub fn parse_variant_attrs<A>(variant: &Variant) -> A
-where
- A: FromVariant,
-{
- match A::from_variant(variant) {
- Ok(attrs) => attrs,
- Err(e) => panic!("failed to parse variant attributes: {}", e),
- }
-}
-
-pub fn ref_pattern<'a>(
- variant: &'a VariantInfo,
- prefix: &str,
-) -> (TokenStream, Vec<BindingInfo<'a>>) {
- let mut v = variant.clone();
- v.bind_with(|_| BindStyle::Ref);
- v.bindings_mut().iter_mut().for_each(|b| {
- b.binding = Ident::new(&format!("{}_{}", b.binding, prefix), Span::call_site())
- });
- (v.pat(), v.bindings().to_vec())
-}
-
-pub fn value<'a>(variant: &'a VariantInfo, prefix: &str) -> (TokenStream, Vec<BindingInfo<'a>>) {
- let mut v = variant.clone();
- v.bindings_mut().iter_mut().for_each(|b| {
- b.binding = Ident::new(&format!("{}_{}", b.binding, prefix), Span::call_site())
- });
- v.bind_with(|_| BindStyle::Move);
- (v.pat(), v.bindings().to_vec())
-}
-
-/// Transforms "FooBar" to "foo-bar".
-///
-/// If the first Camel segment is "Moz", "Webkit", or "Servo", the result string
-/// is prepended with "-".
-pub fn to_css_identifier(mut camel_case: &str) -> String {
- camel_case = camel_case.trim_end_matches('_');
- let mut first = true;
- let mut result = String::with_capacity(camel_case.len());
- while let Some(segment) = split_camel_segment(&mut camel_case) {
- if first {
- match segment {
- "Moz" | "Webkit" | "Servo" => first = false,
- _ => {},
- }
- }
- if !first {
- result.push('-');
- }
- first = false;
- result.push_str(&segment.to_lowercase());
- }
- result
-}
-
-/// Transforms foo-bar to FOO_BAR.
-pub fn to_scream_case(css_case: &str) -> String {
- css_case.to_uppercase().replace('-', "_")
-}
-
-/// Given "FooBar", returns "Foo" and sets `camel_case` to "Bar".
-fn split_camel_segment<'input>(camel_case: &mut &'input str) -> Option<&'input str> {
- let index = match camel_case.chars().next() {
- None => return None,
- Some(ch) => ch.len_utf8(),
- };
- let end_position = camel_case[index..]
- .find(char::is_uppercase)
- .map_or(camel_case.len(), |pos| index + pos);
- let result = &camel_case[..end_position];
- *camel_case = &camel_case[end_position..];
- Some(result)
-}
diff --git a/components/derive_common/lib.rs b/components/derive_common/lib.rs
deleted file mode 100644
index 14415351449..00000000000
--- a/components/derive_common/lib.rs
+++ /dev/null
@@ -1,13 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
-
-extern crate darling;
-extern crate proc_macro2;
-#[macro_use]
-extern crate quote;
-#[macro_use]
-extern crate syn;
-extern crate synstructure;
-
-pub mod cg;
diff --git a/components/derive_common/rustfmt.toml b/components/derive_common/rustfmt.toml
deleted file mode 100644
index c7ad93bafe3..00000000000
--- a/components/derive_common/rustfmt.toml
+++ /dev/null
@@ -1 +0,0 @@
-disable_all_formatting = true
diff --git a/components/geometry/Cargo.toml b/components/geometry/Cargo.toml
index 87a870c0aa5..a816b6f5278 100644
--- a/components/geometry/Cargo.toml
+++ b/components/geometry/Cargo.toml
@@ -13,6 +13,6 @@ path = "lib.rs"
[dependencies]
app_units = { workspace = true }
euclid = { workspace = true }
-malloc_size_of = { path = "../malloc_size_of" }
+malloc_size_of = { workspace = true }
malloc_size_of_derive = { workspace = true }
webrender_api = { workspace = true }
diff --git a/components/gfx/Cargo.toml b/components/gfx/Cargo.toml
index 9538ce11960..114e727d041 100644
--- a/components/gfx/Cargo.toml
+++ b/components/gfx/Cargo.toml
@@ -25,16 +25,16 @@ ipc-channel = { workspace = true }
lazy_static = { workspace = true }
libc = { workspace = true }
log = { workspace = true }
-malloc_size_of = { path = "../malloc_size_of" }
+malloc_size_of = { workspace = true }
net_traits = { workspace = true }
range = { path = "../range" }
serde = { workspace = true }
-servo_arc = { path = "../servo_arc" }
-servo_atoms = { path = "../atoms" }
+servo_arc = { workspace = true }
+servo_atoms = { workspace = true }
servo_url = { path = "../url" }
smallvec = { workspace = true, features = ["union"] }
surfman = { workspace = true }
-style = { path = "../style", features = ["servo"] }
+style = { workspace = true }
ucd = "0.1.1"
unicode-bidi = { workspace = true, features = ["with_serde"] }
unicode-script = { workspace = true }
diff --git a/components/layout/Cargo.toml b/components/layout/Cargo.toml
index 2f4040c9a48..6df0d11c36f 100644
--- a/components/layout/Cargo.toml
+++ b/components/layout/Cargo.toml
@@ -26,7 +26,7 @@ html5ever = { workspace = true }
ipc-channel = { workspace = true }
lazy_static = { workspace = true }
log = { workspace = true }
-malloc_size_of = { path = "../malloc_size_of" }
+malloc_size_of = { workspace = true }
msg = { workspace = true }
net_traits = { workspace = true }
parking_lot = { workspace = true }
@@ -37,14 +37,14 @@ script_layout_interface = { workspace = true }
script_traits = { workspace = true }
serde = { workspace = true }
serde_json = { workspace = true }
-servo_arc = { path = "../servo_arc" }
-servo_atoms = { path = "../atoms" }
+servo_arc = { workspace = true }
+servo_atoms = { workspace = true }
servo_config = { path = "../config" }
servo_geometry = { path = "../geometry" }
servo_url = { path = "../url" }
-size_of_test = { path = "../size_of_test" }
+size_of_test = { workspace = true }
smallvec = { workspace = true, features = ["union"] }
-style = { path = "../style", features = ["servo"] }
+style = { workspace = true }
style_traits = { workspace = true }
unicode-bidi = { workspace = true, features = ["with_serde"] }
unicode-script = { workspace = true }
diff --git a/components/layout_2020/Cargo.toml b/components/layout_2020/Cargo.toml
index e5b647955b8..78ea0bf49a4 100644
--- a/components/layout_2020/Cargo.toml
+++ b/components/layout_2020/Cargo.toml
@@ -36,10 +36,10 @@ script_layout_interface = { workspace = true }
script_traits = { workspace = true }
serde = { workspace = true }
serde_json = { workspace = true }
-servo_arc = { path = "../servo_arc" }
+servo_arc = { workspace = true }
servo_config = { path = "../config" }
servo_url = { path = "../url" }
-style = { path = "../style", features = ["servo"] }
+style = { workspace = true }
style_traits = { workspace = true }
unicode-script = { workspace = true }
unicode-segmentation = { workspace = true }
diff --git a/components/layout_thread/Cargo.toml b/components/layout_thread/Cargo.toml
index 4da4f2d785a..97fc689dd2d 100644
--- a/components/layout_thread/Cargo.toml
+++ b/components/layout_thread/Cargo.toml
@@ -24,7 +24,7 @@ ipc-channel = { workspace = true }
layout = { path = "../layout", package = "layout_2013" }
lazy_static = { workspace = true }
log = { workspace = true }
-malloc_size_of = { path = "../malloc_size_of" }
+malloc_size_of = { workspace = true }
metrics = { path = "../metrics" }
msg = { workspace = true }
net_traits = { workspace = true }
@@ -36,11 +36,11 @@ script_layout_interface = { workspace = true }
script_traits = { workspace = true }
serde_json = { workspace = true }
servo_allocator = { path = "../allocator" }
-servo_arc = { path = "../servo_arc" }
-servo_atoms = { path = "../atoms" }
+servo_arc = { workspace = true }
+servo_atoms = { workspace = true }
servo_config = { path = "../config" }
servo_url = { path = "../url" }
-style = { path = "../style" }
+style = { workspace = true }
style_traits = { workspace = true }
time = { workspace = true }
url = { workspace = true }
diff --git a/components/layout_thread_2020/Cargo.toml b/components/layout_thread_2020/Cargo.toml
index 2a1e510630c..c5010281ae8 100644
--- a/components/layout_thread_2020/Cargo.toml
+++ b/components/layout_thread_2020/Cargo.toml
@@ -23,7 +23,7 @@ ipc-channel = { workspace = true }
layout = { path = "../layout_2020", package = "layout_2020" }
lazy_static = { workspace = true }
log = { workspace = true }
-malloc_size_of = { path = "../malloc_size_of" }
+malloc_size_of = { workspace = true }
metrics = { path = "../metrics" }
msg = { workspace = true }
net_traits = { workspace = true }
@@ -33,11 +33,11 @@ script = { path = "../script" }
script_layout_interface = { workspace = true }
script_traits = { workspace = true }
servo_allocator = { path = "../allocator" }
-servo_arc = { path = "../servo_arc" }
-servo_atoms = { path = "../atoms" }
+servo_arc = { workspace = true }
+servo_atoms = { workspace = true }
servo_config = { path = "../config" }
servo_url = { path = "../url" }
-style = { path = "../style" }
+style = { workspace = true }
style_traits = { workspace = true }
url = { workspace = true }
webrender_api = { workspace = true }
diff --git a/components/malloc_size_of/Cargo.toml b/components/malloc_size_of/Cargo.toml
deleted file mode 100644
index 01204a98e41..00000000000
--- a/components/malloc_size_of/Cargo.toml
+++ /dev/null
@@ -1,52 +0,0 @@
-[package]
-name = "malloc_size_of"
-version = "0.0.1"
-authors = ["The Servo Project Developers"]
-license = "MIT OR Apache-2.0"
-publish = false
-
-[lib]
-path = "lib.rs"
-
-[features]
-servo = [
- "accountable-refcell",
- "content-security-policy",
- "crossbeam-channel",
- "http",
- "keyboard-types",
- "serde",
- "serde_bytes",
- "string_cache",
- "time",
- "url",
- "uuid",
- "webrender_api",
- "xml5ever",
-]
-
-[dependencies]
-accountable-refcell = { workspace = true, optional = true }
-app_units = { workspace = true }
-content-security-policy = { workspace = true, optional = true }
-crossbeam-channel = { workspace = true, optional = true }
-cssparser = { workspace = true }
-euclid = { workspace = true }
-http = { workspace = true, optional = true }
-indexmap = { workspace = true }
-keyboard-types = { workspace = true, optional = true }
-selectors = { path = "../selectors" }
-serde = { workspace = true, optional = true }
-serde_bytes = { workspace = true, optional = true }
-servo_arc = { path = "../servo_arc" }
-smallbitvec = { workspace = true }
-smallvec = { workspace = true }
-string_cache = { workspace = true, optional = true }
-thin-vec = { workspace = true }
-time = { workspace = true, optional = true }
-tokio = { workspace = true, features = ["sync"] }
-url = { workspace = true, optional = true }
-uuid = { workspace = true, optional = true }
-void = "1.0.2"
-webrender_api = { workspace = true, optional = true }
-xml5ever = { workspace = true, optional = true }
diff --git a/components/malloc_size_of/LICENSE-APACHE b/components/malloc_size_of/LICENSE-APACHE
deleted file mode 100644
index 16fe87b06e8..00000000000
--- a/components/malloc_size_of/LICENSE-APACHE
+++ /dev/null
@@ -1,201 +0,0 @@
- Apache License
- Version 2.0, January 2004
- http://www.apache.org/licenses/
-
-TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
-
-1. Definitions.
-
- "License" shall mean the terms and conditions for use, reproduction,
- and distribution as defined by Sections 1 through 9 of this document.
-
- "Licensor" shall mean the copyright owner or entity authorized by
- the copyright owner that is granting the License.
-
- "Legal Entity" shall mean the union of the acting entity and all
- other entities that control, are controlled by, or are under common
- control with that entity. For the purposes of this definition,
- "control" means (i) the power, direct or indirect, to cause the
- direction or management of such entity, whether by contract or
- otherwise, or (ii) ownership of fifty percent (50%) or more of the
- outstanding shares, or (iii) beneficial ownership of such entity.
-
- "You" (or "Your") shall mean an individual or Legal Entity
- exercising permissions granted by this License.
-
- "Source" form shall mean the preferred form for making modifications,
- including but not limited to software source code, documentation
- source, and configuration files.
-
- "Object" form shall mean any form resulting from mechanical
- transformation or translation of a Source form, including but
- not limited to compiled object code, generated documentation,
- and conversions to other media types.
-
- "Work" shall mean the work of authorship, whether in Source or
- Object form, made available under the License, as indicated by a
- copyright notice that is included in or attached to the work
- (an example is provided in the Appendix below).
-
- "Derivative Works" shall mean any work, whether in Source or Object
- form, that is based on (or derived from) the Work and for which the
- editorial revisions, annotations, elaborations, or other modifications
- represent, as a whole, an original work of authorship. For the purposes
- of this License, Derivative Works shall not include works that remain
- separable from, or merely link (or bind by name) to the interfaces of,
- the Work and Derivative Works thereof.
-
- "Contribution" shall mean any work of authorship, including
- the original version of the Work and any modifications or additions
- to that Work or Derivative Works thereof, that is intentionally
- submitted to Licensor for inclusion in the Work by the copyright owner
- or by an individual or Legal Entity authorized to submit on behalf of
- the copyright owner. For the purposes of this definition, "submitted"
- means any form of electronic, verbal, or written communication sent
- to the Licensor or its representatives, including but not limited to
- communication on electronic mailing lists, source code control systems,
- and issue tracking systems that are managed by, or on behalf of, the
- Licensor for the purpose of discussing and improving the Work, but
- excluding communication that is conspicuously marked or otherwise
- designated in writing by the copyright owner as "Not a Contribution."
-
- "Contributor" shall mean Licensor and any individual or Legal Entity
- on behalf of whom a Contribution has been received by Licensor and
- subsequently incorporated within the Work.
-
-2. Grant of Copyright License. Subject to the terms and conditions of
- this License, each Contributor hereby grants to You a perpetual,
- worldwide, non-exclusive, no-charge, royalty-free, irrevocable
- copyright license to reproduce, prepare Derivative Works of,
- publicly display, publicly perform, sublicense, and distribute the
- Work and such Derivative Works in Source or Object form.
-
-3. Grant of Patent License. Subject to the terms and conditions of
- this License, each Contributor hereby grants to You a perpetual,
- worldwide, non-exclusive, no-charge, royalty-free, irrevocable
- (except as stated in this section) patent license to make, have made,
- use, offer to sell, sell, import, and otherwise transfer the Work,
- where such license applies only to those patent claims licensable
- by such Contributor that are necessarily infringed by their
- Contribution(s) alone or by combination of their Contribution(s)
- with the Work to which such Contribution(s) was submitted. If You
- institute patent litigation against any entity (including a
- cross-claim or counterclaim in a lawsuit) alleging that the Work
- or a Contribution incorporated within the Work constitutes direct
- or contributory patent infringement, then any patent licenses
- granted to You under this License for that Work shall terminate
- as of the date such litigation is filed.
-
-4. Redistribution. You may reproduce and distribute copies of the
- Work or Derivative Works thereof in any medium, with or without
- modifications, and in Source or Object form, provided that You
- meet the following conditions:
-
- (a) You must give any other recipients of the Work or
- Derivative Works a copy of this License; and
-
- (b) You must cause any modified files to carry prominent notices
- stating that You changed the files; and
-
- (c) You must retain, in the Source form of any Derivative Works
- that You distribute, all copyright, patent, trademark, and
- attribution notices from the Source form of the Work,
- excluding those notices that do not pertain to any part of
- the Derivative Works; and
-
- (d) If the Work includes a "NOTICE" text file as part of its
- distribution, then any Derivative Works that You distribute must
- include a readable copy of the attribution notices contained
- within such NOTICE file, excluding those notices that do not
- pertain to any part of the Derivative Works, in at least one
- of the following places: within a NOTICE text file distributed
- as part of the Derivative Works; within the Source form or
- documentation, if provided along with the Derivative Works; or,
- within a display generated by the Derivative Works, if and
- wherever such third-party notices normally appear. The contents
- of the NOTICE file are for informational purposes only and
- do not modify the License. You may add Your own attribution
- notices within Derivative Works that You distribute, alongside
- or as an addendum to the NOTICE text from the Work, provided
- that such additional attribution notices cannot be construed
- as modifying the License.
-
- You may add Your own copyright statement to Your modifications and
- may provide additional or different license terms and conditions
- for use, reproduction, or distribution of Your modifications, or
- for any such Derivative Works as a whole, provided Your use,
- reproduction, and distribution of the Work otherwise complies with
- the conditions stated in this License.
-
-5. Submission of Contributions. Unless You explicitly state otherwise,
- any Contribution intentionally submitted for inclusion in the Work
- by You to the Licensor shall be under the terms and conditions of
- this License, without any additional terms or conditions.
- Notwithstanding the above, nothing herein shall supersede or modify
- the terms of any separate license agreement you may have executed
- with Licensor regarding such Contributions.
-
-6. Trademarks. This License does not grant permission to use the trade
- names, trademarks, service marks, or product names of the Licensor,
- except as required for reasonable and customary use in describing the
- origin of the Work and reproducing the content of the NOTICE file.
-
-7. Disclaimer of Warranty. Unless required by applicable law or
- agreed to in writing, Licensor provides the Work (and each
- Contributor provides its Contributions) on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
- implied, including, without limitation, any warranties or conditions
- of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
- PARTICULAR PURPOSE. You are solely responsible for determining the
- appropriateness of using or redistributing the Work and assume any
- risks associated with Your exercise of permissions under this License.
-
-8. Limitation of Liability. In no event and under no legal theory,
- whether in tort (including negligence), contract, or otherwise,
- unless required by applicable law (such as deliberate and grossly
- negligent acts) or agreed to in writing, shall any Contributor be
- liable to You for damages, including any direct, indirect, special,
- incidental, or consequential damages of any character arising as a
- result of this License or out of the use or inability to use the
- Work (including but not limited to damages for loss of goodwill,
- work stoppage, computer failure or malfunction, or any and all
- other commercial damages or losses), even if such Contributor
- has been advised of the possibility of such damages.
-
-9. Accepting Warranty or Additional Liability. While redistributing
- the Work or Derivative Works thereof, You may choose to offer,
- and charge a fee for, acceptance of support, warranty, indemnity,
- or other liability obligations and/or rights consistent with this
- License. However, in accepting such obligations, You may act only
- on Your own behalf and on Your sole responsibility, not on behalf
- of any other Contributor, and only if You agree to indemnify,
- defend, and hold each Contributor harmless for any liability
- incurred by, or claims asserted against, such Contributor by reason
- of your accepting any such warranty or additional liability.
-
-END OF TERMS AND CONDITIONS
-
-APPENDIX: How to apply the Apache License to your work.
-
- To apply the Apache License to your work, attach the following
- boilerplate notice, with the fields enclosed by brackets "[]"
- replaced with your own identifying information. (Don't include
- the brackets!) The text should be enclosed in the appropriate
- comment syntax for the file format. We also recommend that a
- file or class name and description of purpose be included on the
- same "printed page" as the copyright notice for easier
- identification within third-party archives.
-
-Copyright [yyyy] [name of copyright owner]
-
-Licensed under the Apache License, Version 2.0 (the "License");
-you may not use this file except in compliance with the License.
-You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
-Unless required by applicable law or agreed to in writing, software
-distributed under the License is distributed on an "AS IS" BASIS,
-WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-See the License for the specific language governing permissions and
-limitations under the License.
diff --git a/components/malloc_size_of/LICENSE-MIT b/components/malloc_size_of/LICENSE-MIT
deleted file mode 100644
index 31aa79387f2..00000000000
--- a/components/malloc_size_of/LICENSE-MIT
+++ /dev/null
@@ -1,23 +0,0 @@
-Permission is hereby granted, free of charge, to any
-person obtaining a copy of this software and associated
-documentation files (the "Software"), to deal in the
-Software without restriction, including without
-limitation the rights to use, copy, modify, merge,
-publish, distribute, sublicense, and/or sell copies of
-the Software, and to permit persons to whom the Software
-is furnished to do so, subject to the following
-conditions:
-
-The above copyright notice and this permission notice
-shall be included in all copies or substantial portions
-of the Software.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
-ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
-TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
-PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
-SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
-CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
-OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR
-IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
-DEALINGS IN THE SOFTWARE.
diff --git a/components/malloc_size_of/lib.rs b/components/malloc_size_of/lib.rs
deleted file mode 100644
index dd0ca38e17c..00000000000
--- a/components/malloc_size_of/lib.rs
+++ /dev/null
@@ -1,978 +0,0 @@
-// Copyright 2016-2017 The Servo Project Developers. See the COPYRIGHT
-// file at the top-level directory of this distribution and at
-// http://rust-lang.org/COPYRIGHT.
-//
-// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
-// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
-// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
-// option. This file may not be copied, modified, or distributed
-// except according to those terms.
-
-//! A crate for measuring the heap usage of data structures in a way that
-//! integrates with Firefox's memory reporting, particularly the use of
-//! mozjemalloc and DMD. In particular, it has the following features.
-//! - It isn't bound to a particular heap allocator.
-//! - It provides traits for both "shallow" and "deep" measurement, which gives
-//! flexibility in the cases where the traits can't be used.
-//! - It allows for measuring blocks even when only an interior pointer can be
-//! obtained for heap allocations, e.g. `HashSet` and `HashMap`. (This relies
-//! on the heap allocator having suitable support, which mozjemalloc has.)
-//! - It allows handling of types like `Rc` and `Arc` by providing traits that
-//! are different to the ones for non-graph structures.
-//!
-//! Suggested uses are as follows.
-//! - When possible, use the `MallocSizeOf` trait. (Deriving support is
-//! provided by the `malloc_size_of_derive` crate.)
-//! - If you need an additional synchronization argument, provide a function
-//! that is like the standard trait method, but with the extra argument.
-//! - If you need multiple measurements for a type, provide a function named
-//! `add_size_of` that takes a mutable reference to a struct that contains
-//! the multiple measurement fields.
-//! - When deep measurement (via `MallocSizeOf`) cannot be implemented for a
-//! type, shallow measurement (via `MallocShallowSizeOf`) in combination with
-//! iteration can be a useful substitute.
-//! - `Rc` and `Arc` are always tricky, which is why `MallocSizeOf` is not (and
-//! should not be) implemented for them.
-//! - If an `Rc` or `Arc` is known to be a "primary" reference and can always
-//! be measured, it should be measured via the `MallocUnconditionalSizeOf`
-//! trait.
-//! - If an `Rc` or `Arc` should be measured only if it hasn't been seen
-//! before, it should be measured via the `MallocConditionalSizeOf` trait.
-//! - Using universal function call syntax is a good idea when measuring boxed
-//! fields in structs, because it makes it clear that the Box is being
-//! measured as well as the thing it points to. E.g.
-//! `<Box<_> as MallocSizeOf>::size_of(field, ops)`.
-//!
-//! Note: WebRender has a reduced fork of this crate, so that we can avoid
-//! publishing this crate on crates.io.
-
-#[cfg(feature = "servo")]
-extern crate accountable_refcell;
-extern crate app_units;
-#[cfg(feature = "servo")]
-extern crate content_security_policy;
-#[cfg(feature = "servo")]
-extern crate crossbeam_channel;
-extern crate cssparser;
-extern crate euclid;
-#[cfg(feature = "servo")]
-extern crate http;
-#[cfg(feature = "servo")]
-extern crate keyboard_types;
-extern crate selectors;
-#[cfg(feature = "servo")]
-extern crate serde;
-#[cfg(feature = "servo")]
-extern crate serde_bytes;
-extern crate servo_arc;
-extern crate smallbitvec;
-extern crate smallvec;
-#[cfg(feature = "servo")]
-extern crate string_cache;
-#[cfg(feature = "servo")]
-extern crate time;
-#[cfg(feature = "url")]
-extern crate url;
-#[cfg(feature = "servo")]
-extern crate uuid;
-extern crate void;
-#[cfg(feature = "webrender_api")]
-extern crate webrender_api;
-#[cfg(feature = "servo")]
-extern crate xml5ever;
-
-#[cfg(feature = "servo")]
-use content_security_policy as csp;
-#[cfg(feature = "servo")]
-use serde_bytes::ByteBuf;
-use std::hash::{BuildHasher, Hash};
-use std::mem::size_of;
-use std::ops::Range;
-use std::ops::{Deref, DerefMut};
-use std::os::raw::c_void;
-#[cfg(feature = "servo")]
-use uuid::Uuid;
-use void::Void;
-
-/// A C function that takes a pointer to a heap allocation and returns its size.
-type VoidPtrToSizeFn = unsafe extern "C" fn(ptr: *const c_void) -> usize;
-
-/// A closure implementing a stateful predicate on pointers.
-type VoidPtrToBoolFnMut = dyn FnMut(*const c_void) -> bool;
-
-/// Operations used when measuring heap usage of data structures.
-pub struct MallocSizeOfOps {
- /// A function that returns the size of a heap allocation.
- size_of_op: VoidPtrToSizeFn,
-
- /// Like `size_of_op`, but can take an interior pointer. Optional because
- /// not all allocators support this operation. If it's not provided, some
- /// memory measurements will actually be computed estimates rather than
- /// real and accurate measurements.
- enclosing_size_of_op: Option<VoidPtrToSizeFn>,
-
- /// Check if a pointer has been seen before, and remember it for next time.
- /// Useful when measuring `Rc`s and `Arc`s. Optional, because many places
- /// don't need it.
- have_seen_ptr_op: Option<Box<VoidPtrToBoolFnMut>>,
-}
-
-impl MallocSizeOfOps {
- pub fn new(
- size_of: VoidPtrToSizeFn,
- malloc_enclosing_size_of: Option<VoidPtrToSizeFn>,
- have_seen_ptr: Option<Box<VoidPtrToBoolFnMut>>,
- ) -> Self {
- MallocSizeOfOps {
- size_of_op: size_of,
- enclosing_size_of_op: malloc_enclosing_size_of,
- have_seen_ptr_op: have_seen_ptr,
- }
- }
-
- /// Check if an allocation is empty. This relies on knowledge of how Rust
- /// handles empty allocations, which may change in the future.
- fn is_empty<T: ?Sized>(ptr: *const T) -> bool {
- // The correct condition is this:
- // `ptr as usize <= ::std::mem::align_of::<T>()`
- // But we can't call align_of() on a ?Sized T. So we approximate it
- // with the following. 256 is large enough that it should always be
- // larger than the required alignment, but small enough that it is
- // always in the first page of memory and therefore not a legitimate
- // address.
- return ptr as *const usize as usize <= 256;
- }
-
- /// Call `size_of_op` on `ptr`, first checking that the allocation isn't
- /// empty, because some types (such as `Vec`) utilize empty allocations.
- pub unsafe fn malloc_size_of<T: ?Sized>(&self, ptr: *const T) -> usize {
- if MallocSizeOfOps::is_empty(ptr) {
- 0
- } else {
- (self.size_of_op)(ptr as *const c_void)
- }
- }
-
- /// Is an `enclosing_size_of_op` available?
- pub fn has_malloc_enclosing_size_of(&self) -> bool {
- self.enclosing_size_of_op.is_some()
- }
-
- /// Call `enclosing_size_of_op`, which must be available, on `ptr`, which
- /// must not be empty.
- pub unsafe fn malloc_enclosing_size_of<T>(&self, ptr: *const T) -> usize {
- assert!(!MallocSizeOfOps::is_empty(ptr));
- (self.enclosing_size_of_op.unwrap())(ptr as *const c_void)
- }
-
- /// Call `have_seen_ptr_op` on `ptr`.
- pub fn have_seen_ptr<T>(&mut self, ptr: *const T) -> bool {
- let have_seen_ptr_op = self
- .have_seen_ptr_op
- .as_mut()
- .expect("missing have_seen_ptr_op");
- have_seen_ptr_op(ptr as *const c_void)
- }
-}
-
-/// Trait for measuring the "deep" heap usage of a data structure. This is the
-/// most commonly-used of the traits.
-pub trait MallocSizeOf {
- /// Measure the heap usage of all descendant heap-allocated structures, but
- /// not the space taken up by the value itself.
- fn size_of(&self, ops: &mut MallocSizeOfOps) -> usize;
-}
-
-/// Trait for measuring the "shallow" heap usage of a container.
-pub trait MallocShallowSizeOf {
- /// Measure the heap usage of immediate heap-allocated descendant
- /// structures, but not the space taken up by the value itself. Anything
- /// beyond the immediate descendants must be measured separately, using
- /// iteration.
- fn shallow_size_of(&self, ops: &mut MallocSizeOfOps) -> usize;
-}
-
-/// Like `MallocSizeOf`, but with a different name so it cannot be used
-/// accidentally with derive(MallocSizeOf). For use with types like `Rc` and
-/// `Arc` when appropriate (e.g. when measuring a "primary" reference).
-pub trait MallocUnconditionalSizeOf {
- /// Measure the heap usage of all heap-allocated descendant structures, but
- /// not the space taken up by the value itself.
- fn unconditional_size_of(&self, ops: &mut MallocSizeOfOps) -> usize;
-}
-
-/// `MallocUnconditionalSizeOf` combined with `MallocShallowSizeOf`.
-pub trait MallocUnconditionalShallowSizeOf {
- /// `unconditional_size_of` combined with `shallow_size_of`.
- fn unconditional_shallow_size_of(&self, ops: &mut MallocSizeOfOps) -> usize;
-}
-
-/// Like `MallocSizeOf`, but only measures if the value hasn't already been
-/// measured. For use with types like `Rc` and `Arc` when appropriate (e.g.
-/// when there is no "primary" reference).
-pub trait MallocConditionalSizeOf {
- /// Measure the heap usage of all heap-allocated descendant structures, but
- /// not the space taken up by the value itself, and only if that heap usage
- /// hasn't already been measured.
- fn conditional_size_of(&self, ops: &mut MallocSizeOfOps) -> usize;
-}
-
-/// `MallocConditionalSizeOf` combined with `MallocShallowSizeOf`.
-pub trait MallocConditionalShallowSizeOf {
- /// `conditional_size_of` combined with `shallow_size_of`.
- fn conditional_shallow_size_of(&self, ops: &mut MallocSizeOfOps) -> usize;
-}
-
-impl MallocSizeOf for String {
- fn size_of(&self, ops: &mut MallocSizeOfOps) -> usize {
- unsafe { ops.malloc_size_of(self.as_ptr()) }
- }
-}
-
-impl<'a, T: ?Sized> MallocSizeOf for &'a T {
- fn size_of(&self, _ops: &mut MallocSizeOfOps) -> usize {
- // Zero makes sense for a non-owning reference.
- 0
- }
-}
-
-impl<T: ?Sized> MallocShallowSizeOf for Box<T> {
- fn shallow_size_of(&self, ops: &mut MallocSizeOfOps) -> usize {
- unsafe { ops.malloc_size_of(&**self) }
- }
-}
-
-impl<T: MallocSizeOf + ?Sized> MallocSizeOf for Box<T> {
- fn size_of(&self, ops: &mut MallocSizeOfOps) -> usize {
- self.shallow_size_of(ops) + (**self).size_of(ops)
- }
-}
-
-impl MallocSizeOf for () {
- fn size_of(&self, _ops: &mut MallocSizeOfOps) -> usize {
- 0
- }
-}
-
-impl<T1, T2> MallocSizeOf for (T1, T2)
-where
- T1: MallocSizeOf,
- T2: MallocSizeOf,
-{
- fn size_of(&self, ops: &mut MallocSizeOfOps) -> usize {
- self.0.size_of(ops) + self.1.size_of(ops)
- }
-}
-
-impl<T1, T2, T3> MallocSizeOf for (T1, T2, T3)
-where
- T1: MallocSizeOf,
- T2: MallocSizeOf,
- T3: MallocSizeOf,
-{
- fn size_of(&self, ops: &mut MallocSizeOfOps) -> usize {
- self.0.size_of(ops) + self.1.size_of(ops) + self.2.size_of(ops)
- }
-}
-
-impl<T1, T2, T3, T4> MallocSizeOf for (T1, T2, T3, T4)
-where
- T1: MallocSizeOf,
- T2: MallocSizeOf,
- T3: MallocSizeOf,
- T4: MallocSizeOf,
-{
- fn size_of(&self, ops: &mut MallocSizeOfOps) -> usize {
- self.0.size_of(ops) + self.1.size_of(ops) + self.2.size_of(ops) + self.3.size_of(ops)
- }
-}
-
-impl<T: MallocSizeOf> MallocSizeOf for Option<T> {
- fn size_of(&self, ops: &mut MallocSizeOfOps) -> usize {
- if let Some(val) = self.as_ref() {
- val.size_of(ops)
- } else {
- 0
- }
- }
-}
-
-impl<T: MallocSizeOf, E: MallocSizeOf> MallocSizeOf for Result<T, E> {
- fn size_of(&self, ops: &mut MallocSizeOfOps) -> usize {
- match *self {
- Ok(ref x) => x.size_of(ops),
- Err(ref e) => e.size_of(ops),
- }
- }
-}
-
-impl<T: MallocSizeOf + Copy> MallocSizeOf for std::cell::Cell<T> {
- fn size_of(&self, ops: &mut MallocSizeOfOps) -> usize {
- self.get().size_of(ops)
- }
-}
-
-impl<T: MallocSizeOf> MallocSizeOf for std::cell::RefCell<T> {
- fn size_of(&self, ops: &mut MallocSizeOfOps) -> usize {
- self.borrow().size_of(ops)
- }
-}
-
-impl<'a, B: ?Sized + ToOwned> MallocSizeOf for std::borrow::Cow<'a, B>
-where
- B::Owned: MallocSizeOf,
-{
- fn size_of(&self, ops: &mut MallocSizeOfOps) -> usize {
- match *self {
- std::borrow::Cow::Borrowed(_) => 0,
- std::borrow::Cow::Owned(ref b) => b.size_of(ops),
- }
- }
-}
-
-impl<T: MallocSizeOf> MallocSizeOf for [T] {
- fn size_of(&self, ops: &mut MallocSizeOfOps) -> usize {
- let mut n = 0;
- for elem in self.iter() {
- n += elem.size_of(ops);
- }
- n
- }
-}
-
-#[cfg(feature = "servo")]
-impl MallocShallowSizeOf for ByteBuf {
- fn shallow_size_of(&self, ops: &mut MallocSizeOfOps) -> usize {
- unsafe { ops.malloc_size_of(self.as_ptr()) }
- }
-}
-
-#[cfg(feature = "servo")]
-impl MallocSizeOf for ByteBuf {
- fn size_of(&self, ops: &mut MallocSizeOfOps) -> usize {
- let mut n = self.shallow_size_of(ops);
- for elem in self.iter() {
- n += elem.size_of(ops);
- }
- n
- }
-}
-
-impl<T> MallocShallowSizeOf for Vec<T> {
- fn shallow_size_of(&self, ops: &mut MallocSizeOfOps) -> usize {
- unsafe { ops.malloc_size_of(self.as_ptr()) }
- }
-}
-
-impl<T: MallocSizeOf> MallocSizeOf for Vec<T> {
- fn size_of(&self, ops: &mut MallocSizeOfOps) -> usize {
- let mut n = self.shallow_size_of(ops);
- for elem in self.iter() {
- n += elem.size_of(ops);
- }
- n
- }
-}
-
-impl<T> MallocShallowSizeOf for std::collections::VecDeque<T> {
- fn shallow_size_of(&self, ops: &mut MallocSizeOfOps) -> usize {
- if ops.has_malloc_enclosing_size_of() {
- if let Some(front) = self.front() {
- // The front element is an interior pointer.
- unsafe { ops.malloc_enclosing_size_of(&*front) }
- } else {
- // This assumes that no memory is allocated when the VecDeque is empty.
- 0
- }
- } else {
- // An estimate.
- self.capacity() * size_of::<T>()
- }
- }
-}
-
-impl<T: MallocSizeOf> MallocSizeOf for std::collections::VecDeque<T> {
- fn size_of(&self, ops: &mut MallocSizeOfOps) -> usize {
- let mut n = self.shallow_size_of(ops);
- for elem in self.iter() {
- n += elem.size_of(ops);
- }
- n
- }
-}
-
-impl<A: smallvec::Array> MallocShallowSizeOf for smallvec::SmallVec<A> {
- fn shallow_size_of(&self, ops: &mut MallocSizeOfOps) -> usize {
- if self.spilled() {
- unsafe { ops.malloc_size_of(self.as_ptr()) }
- } else {
- 0
- }
- }
-}
-
-impl<A> MallocSizeOf for smallvec::SmallVec<A>
-where
- A: smallvec::Array,
- A::Item: MallocSizeOf,
-{
- fn size_of(&self, ops: &mut MallocSizeOfOps) -> usize {
- let mut n = self.shallow_size_of(ops);
- for elem in self.iter() {
- n += elem.size_of(ops);
- }
- n
- }
-}
-
-impl<T> MallocShallowSizeOf for thin_vec::ThinVec<T> {
- fn shallow_size_of(&self, ops: &mut MallocSizeOfOps) -> usize {
- if self.capacity() == 0 {
- // If it's the singleton we might not be a heap pointer.
- return 0;
- }
-
- assert_eq!(
- std::mem::size_of::<Self>(),
- std::mem::size_of::<*const ()>()
- );
- unsafe { ops.malloc_size_of(*(self as *const Self as *const *const ())) }
- }
-}
-
-impl<T: MallocSizeOf> MallocSizeOf for thin_vec::ThinVec<T> {
- fn size_of(&self, ops: &mut MallocSizeOfOps) -> usize {
- let mut n = self.shallow_size_of(ops);
- for elem in self.iter() {
- n += elem.size_of(ops);
- }
- n
- }
-}
-
-macro_rules! malloc_size_of_hash_set {
- ($ty:ty) => {
- impl<T, S> MallocShallowSizeOf for $ty
- where
- T: Eq + Hash,
- S: BuildHasher,
- {
- fn shallow_size_of(&self, ops: &mut MallocSizeOfOps) -> usize {
- if ops.has_malloc_enclosing_size_of() {
- // The first value from the iterator gives us an interior pointer.
- // `ops.malloc_enclosing_size_of()` then gives us the storage size.
- // This assumes that the `HashSet`'s contents (values and hashes)
- // are all stored in a single contiguous heap allocation.
- self.iter()
- .next()
- .map_or(0, |t| unsafe { ops.malloc_enclosing_size_of(t) })
- } else {
- // An estimate.
- self.capacity() * (size_of::<T>() + size_of::<usize>())
- }
- }
- }
-
- impl<T, S> MallocSizeOf for $ty
- where
- T: Eq + Hash + MallocSizeOf,
- S: BuildHasher,
- {
- fn size_of(&self, ops: &mut MallocSizeOfOps) -> usize {
- let mut n = self.shallow_size_of(ops);
- for t in self.iter() {
- n += t.size_of(ops);
- }
- n
- }
- }
- };
-}
-
-malloc_size_of_hash_set!(std::collections::HashSet<T, S>);
-malloc_size_of_hash_set!(indexmap::IndexSet<T, S>);
-
-macro_rules! malloc_size_of_hash_map {
- ($ty:ty) => {
- impl<K, V, S> MallocShallowSizeOf for $ty
- where
- K: Eq + Hash,
- S: BuildHasher,
- {
- fn shallow_size_of(&self, ops: &mut MallocSizeOfOps) -> usize {
- // See the implementation for std::collections::HashSet for details.
- if ops.has_malloc_enclosing_size_of() {
- self.values()
- .next()
- .map_or(0, |v| unsafe { ops.malloc_enclosing_size_of(v) })
- } else {
- self.capacity() * (size_of::<V>() + size_of::<K>() + size_of::<usize>())
- }
- }
- }
-
- impl<K, V, S> MallocSizeOf for $ty
- where
- K: Eq + Hash + MallocSizeOf,
- V: MallocSizeOf,
- S: BuildHasher,
- {
- fn size_of(&self, ops: &mut MallocSizeOfOps) -> usize {
- let mut n = self.shallow_size_of(ops);
- for (k, v) in self.iter() {
- n += k.size_of(ops);
- n += v.size_of(ops);
- }
- n
- }
- }
- };
-}
-
-malloc_size_of_hash_map!(std::collections::HashMap<K, V, S>);
-malloc_size_of_hash_map!(indexmap::IndexMap<K, V, S>);
-
-impl<K, V> MallocShallowSizeOf for std::collections::BTreeMap<K, V>
-where
- K: Eq + Hash,
-{
- fn shallow_size_of(&self, ops: &mut MallocSizeOfOps) -> usize {
- if ops.has_malloc_enclosing_size_of() {
- self.values()
- .next()
- .map_or(0, |v| unsafe { ops.malloc_enclosing_size_of(v) })
- } else {
- self.len() * (size_of::<V>() + size_of::<K>() + size_of::<usize>())
- }
- }
-}
-
-impl<K, V> MallocSizeOf for std::collections::BTreeMap<K, V>
-where
- K: Eq + Hash + MallocSizeOf,
- V: MallocSizeOf,
-{
- fn size_of(&self, ops: &mut MallocSizeOfOps) -> usize {
- let mut n = self.shallow_size_of(ops);
- for (k, v) in self.iter() {
- n += k.size_of(ops);
- n += v.size_of(ops);
- }
- n
- }
-}
-
-// PhantomData is always 0.
-impl<T> MallocSizeOf for std::marker::PhantomData<T> {
- fn size_of(&self, _ops: &mut MallocSizeOfOps) -> usize {
- 0
- }
-}
-
-// XXX: we don't want MallocSizeOf to be defined for Rc and Arc. If negative
-// trait bounds are ever allowed, this code should be uncommented.
-// (We do have a compile-fail test for this:
-// rc_arc_must_not_derive_malloc_size_of.rs)
-//impl<T> !MallocSizeOf for Arc<T> { }
-//impl<T> !MallocShallowSizeOf for Arc<T> { }
-
-impl<T> MallocUnconditionalShallowSizeOf for servo_arc::Arc<T> {
- fn unconditional_shallow_size_of(&self, ops: &mut MallocSizeOfOps) -> usize {
- unsafe { ops.malloc_size_of(self.heap_ptr()) }
- }
-}
-
-impl<T: MallocSizeOf> MallocUnconditionalSizeOf for servo_arc::Arc<T> {
- fn unconditional_size_of(&self, ops: &mut MallocSizeOfOps) -> usize {
- self.unconditional_shallow_size_of(ops) + (**self).size_of(ops)
- }
-}
-
-impl<T> MallocConditionalShallowSizeOf for servo_arc::Arc<T> {
- fn conditional_shallow_size_of(&self, ops: &mut MallocSizeOfOps) -> usize {
- if ops.have_seen_ptr(self.heap_ptr()) {
- 0
- } else {
- self.unconditional_shallow_size_of(ops)
- }
- }
-}
-
-impl<T: MallocSizeOf> MallocConditionalSizeOf for servo_arc::Arc<T> {
- fn conditional_size_of(&self, ops: &mut MallocSizeOfOps) -> usize {
- if ops.have_seen_ptr(self.heap_ptr()) {
- 0
- } else {
- self.unconditional_size_of(ops)
- }
- }
-}
-
-/// If a mutex is stored directly as a member of a data type that is being measured,
-/// it is the unique owner of its contents and deserves to be measured.
-///
-/// If a mutex is stored inside of an Arc value as a member of a data type that is being measured,
-/// the Arc will not be automatically measured so there is no risk of overcounting the mutex's
-/// contents.
-impl<T: MallocSizeOf> MallocSizeOf for std::sync::Mutex<T> {
- fn size_of(&self, ops: &mut MallocSizeOfOps) -> usize {
- (*self.lock().unwrap()).size_of(ops)
- }
-}
-
-impl MallocSizeOf for smallbitvec::SmallBitVec {
- fn size_of(&self, ops: &mut MallocSizeOfOps) -> usize {
- if let Some(ptr) = self.heap_ptr() {
- unsafe { ops.malloc_size_of(ptr) }
- } else {
- 0
- }
- }
-}
-
-impl<T: MallocSizeOf, Unit> MallocSizeOf for euclid::Length<T, Unit> {
- fn size_of(&self, ops: &mut MallocSizeOfOps) -> usize {
- self.0.size_of(ops)
- }
-}
-
-impl<T: MallocSizeOf, Src, Dst> MallocSizeOf for euclid::Scale<T, Src, Dst> {
- fn size_of(&self, ops: &mut MallocSizeOfOps) -> usize {
- self.0.size_of(ops)
- }
-}
-
-impl<T: MallocSizeOf, U> MallocSizeOf for euclid::Point2D<T, U> {
- fn size_of(&self, ops: &mut MallocSizeOfOps) -> usize {
- self.x.size_of(ops) + self.y.size_of(ops)
- }
-}
-
-impl<T: MallocSizeOf, U> MallocSizeOf for euclid::Rect<T, U> {
- fn size_of(&self, ops: &mut MallocSizeOfOps) -> usize {
- self.origin.size_of(ops) + self.size.size_of(ops)
- }
-}
-
-impl<T: MallocSizeOf, U> MallocSizeOf for euclid::SideOffsets2D<T, U> {
- fn size_of(&self, ops: &mut MallocSizeOfOps) -> usize {
- self.top.size_of(ops) +
- self.right.size_of(ops) +
- self.bottom.size_of(ops) +
- self.left.size_of(ops)
- }
-}
-
-impl<T: MallocSizeOf, U> MallocSizeOf for euclid::Size2D<T, U> {
- fn size_of(&self, ops: &mut MallocSizeOfOps) -> usize {
- self.width.size_of(ops) + self.height.size_of(ops)
- }
-}
-
-impl<T: MallocSizeOf, Src, Dst> MallocSizeOf for euclid::Transform2D<T, Src, Dst> {
- fn size_of(&self, ops: &mut MallocSizeOfOps) -> usize {
- self.m11.size_of(ops) +
- self.m12.size_of(ops) +
- self.m21.size_of(ops) +
- self.m22.size_of(ops) +
- self.m31.size_of(ops) +
- self.m32.size_of(ops)
- }
-}
-
-impl<T: MallocSizeOf, Src, Dst> MallocSizeOf for euclid::Transform3D<T, Src, Dst> {
- fn size_of(&self, ops: &mut MallocSizeOfOps) -> usize {
- self.m11.size_of(ops) +
- self.m12.size_of(ops) +
- self.m13.size_of(ops) +
- self.m14.size_of(ops) +
- self.m21.size_of(ops) +
- self.m22.size_of(ops) +
- self.m23.size_of(ops) +
- self.m24.size_of(ops) +
- self.m31.size_of(ops) +
- self.m32.size_of(ops) +
- self.m33.size_of(ops) +
- self.m34.size_of(ops) +
- self.m41.size_of(ops) +
- self.m42.size_of(ops) +
- self.m43.size_of(ops) +
- self.m44.size_of(ops)
- }
-}
-
-impl<T: MallocSizeOf, U> MallocSizeOf for euclid::Vector2D<T, U> {
- fn size_of(&self, ops: &mut MallocSizeOfOps) -> usize {
- self.x.size_of(ops) + self.y.size_of(ops)
- }
-}
-
-impl MallocSizeOf for selectors::parser::AncestorHashes {
- fn size_of(&self, ops: &mut MallocSizeOfOps) -> usize {
- let selectors::parser::AncestorHashes { ref packed_hashes } = *self;
- packed_hashes.size_of(ops)
- }
-}
-
-impl<Impl: selectors::parser::SelectorImpl> MallocSizeOf for selectors::parser::Selector<Impl>
-where
- Impl::NonTSPseudoClass: MallocSizeOf,
- Impl::PseudoElement: MallocSizeOf,
-{
- fn size_of(&self, ops: &mut MallocSizeOfOps) -> usize {
- let mut n = 0;
-
- // It's OK to measure this ThinArc directly because it's the
- // "primary" reference. (The secondary references are on the
- // Stylist.)
- n += unsafe { ops.malloc_size_of(self.thin_arc_heap_ptr()) };
- for component in self.iter_raw_match_order() {
- n += component.size_of(ops);
- }
-
- n
- }
-}
-
-impl<Impl: selectors::parser::SelectorImpl> MallocSizeOf for selectors::parser::Component<Impl>
-where
- Impl::NonTSPseudoClass: MallocSizeOf,
- Impl::PseudoElement: MallocSizeOf,
-{
- fn size_of(&self, ops: &mut MallocSizeOfOps) -> usize {
- use selectors::parser::Component;
-
- match self {
- Component::AttributeOther(ref attr_selector) => attr_selector.size_of(ops),
- Component::Negation(ref components) => components.size_of(ops),
- Component::NonTSPseudoClass(ref pseudo) => (*pseudo).size_of(ops),
- Component::Slotted(ref selector) | Component::Host(Some(ref selector)) => {
- selector.size_of(ops)
- },
- Component::Is(ref list) | Component::Where(ref list) => list.size_of(ops),
- Component::Has(ref relative_selectors) => relative_selectors.size_of(ops),
- Component::NthOf(ref nth_of_data) => nth_of_data.size_of(ops),
- Component::PseudoElement(ref pseudo) => (*pseudo).size_of(ops),
- Component::Combinator(..) |
- Component::ExplicitAnyNamespace |
- Component::ExplicitNoNamespace |
- Component::DefaultNamespace(..) |
- Component::Namespace(..) |
- Component::ExplicitUniversalType |
- Component::LocalName(..) |
- Component::ID(..) |
- Component::Part(..) |
- Component::Class(..) |
- Component::AttributeInNoNamespaceExists { .. } |
- Component::AttributeInNoNamespace { .. } |
- Component::Root |
- Component::Empty |
- Component::Scope |
- Component::ParentSelector |
- Component::Nth(..) |
- Component::Host(None) |
- Component::RelativeSelectorAnchor => 0,
- }
- }
-}
-
-impl<Impl: selectors::parser::SelectorImpl> MallocSizeOf
- for selectors::attr::AttrSelectorWithOptionalNamespace<Impl>
-{
- fn size_of(&self, _ops: &mut MallocSizeOfOps) -> usize {
- 0
- }
-}
-
-impl MallocSizeOf for Void {
- #[inline]
- fn size_of(&self, _ops: &mut MallocSizeOfOps) -> usize {
- void::unreachable(*self)
- }
-}
-
-#[cfg(feature = "servo")]
-impl<Static: string_cache::StaticAtomSet> MallocSizeOf for string_cache::Atom<Static> {
- fn size_of(&self, _ops: &mut MallocSizeOfOps) -> usize {
- 0
- }
-}
-
-/// For use on types where size_of() returns 0.
-#[macro_export]
-macro_rules! malloc_size_of_is_0(
- ($($ty:ty),+) => (
- $(
- impl $crate::MallocSizeOf for $ty {
- #[inline(always)]
- fn size_of(&self, _: &mut $crate::MallocSizeOfOps) -> usize {
- 0
- }
- }
- )+
- );
- ($($ty:ident<$($gen:ident),+>),+) => (
- $(
- impl<$($gen: $crate::MallocSizeOf),+> $crate::MallocSizeOf for $ty<$($gen),+> {
- #[inline(always)]
- fn size_of(&self, _: &mut $crate::MallocSizeOfOps) -> usize {
- 0
- }
- }
- )+
- );
-);
-
-malloc_size_of_is_0!(bool, char, str);
-malloc_size_of_is_0!(u8, u16, u32, u64, u128, usize);
-malloc_size_of_is_0!(i8, i16, i32, i64, i128, isize);
-malloc_size_of_is_0!(f32, f64);
-malloc_size_of_is_0!(std::num::NonZeroU64);
-
-malloc_size_of_is_0!(std::sync::atomic::AtomicBool);
-malloc_size_of_is_0!(std::sync::atomic::AtomicIsize);
-malloc_size_of_is_0!(std::sync::atomic::AtomicUsize);
-
-malloc_size_of_is_0!(Range<u8>, Range<u16>, Range<u32>, Range<u64>, Range<usize>);
-malloc_size_of_is_0!(Range<i8>, Range<i16>, Range<i32>, Range<i64>, Range<isize>);
-malloc_size_of_is_0!(Range<f32>, Range<f64>);
-
-malloc_size_of_is_0!(app_units::Au);
-
-malloc_size_of_is_0!(cssparser::RGBA, cssparser::TokenSerializationType);
-
-#[cfg(feature = "servo")]
-malloc_size_of_is_0!(csp::Destination);
-
-#[cfg(feature = "servo")]
-malloc_size_of_is_0!(Uuid);
-
-#[cfg(feature = "url")]
-impl MallocSizeOf for url::Host {
- fn size_of(&self, ops: &mut MallocSizeOfOps) -> usize {
- match *self {
- url::Host::Domain(ref s) => s.size_of(ops),
- _ => 0,
- }
- }
-}
-#[cfg(feature = "webrender_api")]
-malloc_size_of_is_0!(webrender_api::BorderRadius);
-#[cfg(feature = "webrender_api")]
-malloc_size_of_is_0!(webrender_api::BorderStyle);
-#[cfg(feature = "webrender_api")]
-malloc_size_of_is_0!(webrender_api::BoxShadowClipMode);
-#[cfg(feature = "webrender_api")]
-malloc_size_of_is_0!(webrender_api::ColorF);
-#[cfg(feature = "webrender_api")]
-malloc_size_of_is_0!(webrender_api::ComplexClipRegion);
-#[cfg(feature = "webrender_api")]
-malloc_size_of_is_0!(webrender_api::ExtendMode);
-#[cfg(feature = "webrender_api")]
-malloc_size_of_is_0!(webrender_api::FilterOp);
-#[cfg(feature = "webrender_api")]
-malloc_size_of_is_0!(webrender_api::ExternalScrollId);
-#[cfg(feature = "webrender_api")]
-malloc_size_of_is_0!(webrender_api::FontInstanceKey);
-#[cfg(feature = "webrender_api")]
-malloc_size_of_is_0!(webrender_api::GradientStop);
-#[cfg(feature = "webrender_api")]
-malloc_size_of_is_0!(webrender_api::GlyphInstance);
-#[cfg(feature = "webrender_api")]
-malloc_size_of_is_0!(webrender_api::NinePatchBorder);
-#[cfg(feature = "webrender_api")]
-malloc_size_of_is_0!(webrender_api::ImageKey);
-#[cfg(feature = "webrender_api")]
-malloc_size_of_is_0!(webrender_api::ImageRendering);
-#[cfg(feature = "webrender_api")]
-malloc_size_of_is_0!(webrender_api::LineStyle);
-#[cfg(feature = "webrender_api")]
-malloc_size_of_is_0!(webrender_api::MixBlendMode);
-#[cfg(feature = "webrender_api")]
-malloc_size_of_is_0!(webrender_api::NormalBorder);
-#[cfg(feature = "webrender_api")]
-malloc_size_of_is_0!(webrender_api::RepeatMode);
-#[cfg(feature = "webrender_api")]
-malloc_size_of_is_0!(webrender_api::StickyOffsetBounds);
-#[cfg(feature = "webrender_api")]
-malloc_size_of_is_0!(webrender_api::TransformStyle);
-
-#[cfg(feature = "servo")]
-impl MallocSizeOf for keyboard_types::Key {
- fn size_of(&self, ops: &mut MallocSizeOfOps) -> usize {
- match self {
- keyboard_types::Key::Character(ref s) => s.size_of(ops),
- _ => 0,
- }
- }
-}
-
-#[cfg(feature = "servo")]
-malloc_size_of_is_0!(keyboard_types::Modifiers);
-
-#[cfg(feature = "servo")]
-impl MallocSizeOf for xml5ever::QualName {
- fn size_of(&self, ops: &mut MallocSizeOfOps) -> usize {
- self.prefix.size_of(ops) + self.ns.size_of(ops) + self.local.size_of(ops)
- }
-}
-
-#[cfg(feature = "servo")]
-malloc_size_of_is_0!(time::Duration);
-#[cfg(feature = "servo")]
-malloc_size_of_is_0!(time::Tm);
-#[cfg(feature = "servo")]
-malloc_size_of_is_0!(std::time::Duration);
-#[cfg(feature = "servo")]
-malloc_size_of_is_0!(std::time::SystemTime);
-#[cfg(feature = "servo")]
-malloc_size_of_is_0!(std::time::Instant);
-
-// Placeholder for unique case where internals of Sender cannot be measured.
-// malloc size of is 0 macro complains about type supplied!
-#[cfg(feature = "servo")]
-impl<T> MallocSizeOf for crossbeam_channel::Sender<T> {
- fn size_of(&self, _ops: &mut MallocSizeOfOps) -> usize {
- 0
- }
-}
-
-#[cfg(feature = "servo")]
-impl<T> MallocSizeOf for tokio::sync::mpsc::UnboundedSender<T> {
- fn size_of(&self, _ops: &mut MallocSizeOfOps) -> usize {
- 0
- }
-}
-
-#[cfg(feature = "servo")]
-impl MallocSizeOf for http::StatusCode {
- fn size_of(&self, _ops: &mut MallocSizeOfOps) -> usize {
- 0
- }
-}
-
-/// Measurable that defers to inner value and used to verify MallocSizeOf implementation in a
-/// struct.
-#[derive(Clone)]
-pub struct Measurable<T: MallocSizeOf>(pub T);
-
-impl<T: MallocSizeOf> Deref for Measurable<T> {
- type Target = T;
-
- fn deref(&self) -> &T {
- &self.0
- }
-}
-
-impl<T: MallocSizeOf> DerefMut for Measurable<T> {
- fn deref_mut(&mut self) -> &mut T {
- &mut self.0
- }
-}
-
-#[cfg(feature = "servo")]
-impl<T: MallocSizeOf> MallocSizeOf for accountable_refcell::RefCell<T> {
- fn size_of(&self, ops: &mut MallocSizeOfOps) -> usize {
- self.borrow().size_of(ops)
- }
-}
diff --git a/components/malloc_size_of/rustfmt.toml b/components/malloc_size_of/rustfmt.toml
deleted file mode 100644
index c7ad93bafe3..00000000000
--- a/components/malloc_size_of/rustfmt.toml
+++ /dev/null
@@ -1 +0,0 @@
-disable_all_formatting = true
diff --git a/components/metrics/Cargo.toml b/components/metrics/Cargo.toml
index 29579d9aacb..08701edf372 100644
--- a/components/metrics/Cargo.toml
+++ b/components/metrics/Cargo.toml
@@ -14,7 +14,7 @@ path = "lib.rs"
gfx_traits = { workspace = true }
ipc-channel = { workspace = true }
log = { workspace = true }
-malloc_size_of = { path = "../malloc_size_of" }
+malloc_size_of = { workspace = true }
malloc_size_of_derive = { workspace = true }
msg = { workspace = true }
profile_traits = { workspace = true }
diff --git a/components/net/Cargo.toml b/components/net/Cargo.toml
index 0d188ba4d13..01da269cd0b 100644
--- a/components/net/Cargo.toml
+++ b/components/net/Cargo.toml
@@ -38,7 +38,7 @@ ipc-channel = { workspace = true }
lazy_static = { workspace = true }
libflate = "0.1"
log = { workspace = true }
-malloc_size_of = { path = "../malloc_size_of" }
+malloc_size_of = { workspace = true }
malloc_size_of_derive = { workspace = true }
mime = { workspace = true }
mime_guess = { workspace = true }
@@ -53,7 +53,7 @@ rustls-pemfile = { workspace = true }
serde = { workspace = true }
serde_json = { workspace = true }
servo_allocator = { path = "../allocator" }
-servo_arc = { path = "../servo_arc" }
+servo_arc = { workspace = true }
servo_config = { path = "../config" }
servo_url = { path = "../url" }
sha2 = "0.10"
diff --git a/components/pixels/Cargo.toml b/components/pixels/Cargo.toml
index 708b16163ba..f201ff7c0e4 100644
--- a/components/pixels/Cargo.toml
+++ b/components/pixels/Cargo.toml
@@ -12,6 +12,6 @@ path = "lib.rs"
[dependencies]
euclid = { workspace = true }
-malloc_size_of = { path = "../malloc_size_of" }
+malloc_size_of = { workspace = true }
malloc_size_of_derive = { workspace = true }
serde = { workspace = true, features = ["derive"] }
diff --git a/components/range/Cargo.toml b/components/range/Cargo.toml
index bcaf874786f..c674d5a799c 100644
--- a/components/range/Cargo.toml
+++ b/components/range/Cargo.toml
@@ -11,7 +11,7 @@ name = "range"
path = "lib.rs"
[dependencies]
-malloc_size_of = { path = "../malloc_size_of" }
+malloc_size_of = { workspace = true }
malloc_size_of_derive = { workspace = true }
num-traits = { workspace = true }
serde = { workspace = true }
diff --git a/components/script/Cargo.toml b/components/script/Cargo.toml
index cc29865bd69..b9e9ff1249c 100644
--- a/components/script/Cargo.toml
+++ b/components/script/Cargo.toml
@@ -68,7 +68,7 @@ keyboard-types = { workspace = true }
lazy_static = { workspace = true }
libc = { workspace = true }
log = { workspace = true }
-malloc_size_of = { path = "../malloc_size_of" }
+malloc_size_of = { workspace = true }
malloc_size_of_derive = { workspace = true }
media = { path = "../media" }
metrics = { path = "../metrics" }
@@ -88,20 +88,20 @@ ref_filter_map = "1.0.1"
regex = { workspace = true }
script_layout_interface = { workspace = true }
script_traits = { workspace = true }
-selectors = { path = "../selectors" }
+selectors = { workspace = true }
serde = { workspace = true, features = ["derive"] }
serde_bytes = { workspace = true }
servo-media = { git = "https://github.com/servo/media" }
servo_allocator = { path = "../allocator" }
-servo_arc = { path = "../servo_arc" }
-servo_atoms = { path = "../atoms" }
+servo_arc = { workspace = true }
+servo_atoms = { workspace = true }
servo_config = { path = "../config" }
servo_geometry = { path = "../geometry" }
servo_rand = { path = "../rand" }
servo_url = { path = "../url" }
smallvec = { workspace = true, features = ["union"] }
sparkle = { workspace = true }
-style = { path = "../style", features = ["servo"] }
+style = { workspace = true }
style_traits = { workspace = true }
swapper = "0.1"
tempfile = "3"
diff --git a/components/selectors/Cargo.toml b/components/selectors/Cargo.toml
deleted file mode 100644
index da1d0e509d8..00000000000
--- a/components/selectors/Cargo.toml
+++ /dev/null
@@ -1,36 +0,0 @@
-[package]
-name = "selectors"
-version = "0.24.0"
-authors = ["The Servo Project Developers"]
-documentation = "https://docs.rs/selectors/"
-description = "CSS Selectors matching for Rust"
-repository = "https://github.com/servo/servo"
-readme = "README.md"
-keywords = ["css", "selectors"]
-license = "MPL-2.0"
-build = "build.rs"
-
-[lib]
-name = "selectors"
-path = "lib.rs"
-
-[features]
-bench = []
-
-[dependencies]
-bitflags = "1.0"
-cssparser = { workspace = true }
-derive_more = { version = "0.99", default-features = false, features = ["add", "add_assign"] }
-fxhash = "0.2"
-log = "0.4"
-new_debug_unreachable = "1"
-phf = "0.10"
-precomputed-hash = "0.1"
-servo_arc = { version = "0.2", path = "../servo_arc" }
-size_of_test = { path = "../size_of_test" }
-smallvec = "1.0"
-to_shmem = { version = "0.0.1", path = "../to_shmem" }
-to_shmem_derive = { version = "0.0.1", path = "../to_shmem_derive" }
-
-[build-dependencies]
-phf_codegen = "0.10"
diff --git a/components/selectors/README.md b/components/selectors/README.md
deleted file mode 100644
index dac4a7ff91a..00000000000
--- a/components/selectors/README.md
+++ /dev/null
@@ -1,25 +0,0 @@
-rust-selectors
-==============
-
-* [![Build Status](https://travis-ci.com/servo/rust-selectors.svg?branch=master)](
- https://travis-ci.com/servo/rust-selectors)
-* [Documentation](https://docs.rs/selectors/)
-* [crates.io](https://crates.io/crates/selectors)
-
-CSS Selectors library for Rust.
-Includes parsing and serilization of selectors,
-as well as matching against a generic tree of elements.
-Pseudo-elements and most pseudo-classes are generic as well.
-
-**Warning:** breaking changes are made to this library fairly frequently
-(13 times in 2016, for example).
-However you can use this crate without updating it that often,
-old versions stay available on crates.io and Cargo will only automatically update
-to versions that are numbered as compatible.
-
-To see how to use this library with your own tree representation,
-see [Kuchiki’s `src/select.rs`](https://github.com/kuchiki-rs/kuchiki/blob/master/src/select.rs).
-(Note however that Kuchiki is not always up to date with the latest rust-selectors version,
-so that code may need to be tweaked.)
-If you don’t already have a tree data structure,
-consider using [Kuchiki](https://github.com/kuchiki-rs/kuchiki) itself.
diff --git a/components/selectors/attr.rs b/components/selectors/attr.rs
deleted file mode 100644
index e3122ea701e..00000000000
--- a/components/selectors/attr.rs
+++ /dev/null
@@ -1,192 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
-
-use crate::parser::SelectorImpl;
-use cssparser::ToCss;
-use std::fmt;
-
-#[derive(Clone, Eq, PartialEq, ToShmem)]
-#[shmem(no_bounds)]
-pub struct AttrSelectorWithOptionalNamespace<Impl: SelectorImpl> {
- #[shmem(field_bound)]
- pub namespace: Option<NamespaceConstraint<(Impl::NamespacePrefix, Impl::NamespaceUrl)>>,
- #[shmem(field_bound)]
- pub local_name: Impl::LocalName,
- pub local_name_lower: Impl::LocalName,
- #[shmem(field_bound)]
- pub operation: ParsedAttrSelectorOperation<Impl::AttrValue>,
-}
-
-impl<Impl: SelectorImpl> AttrSelectorWithOptionalNamespace<Impl> {
- pub fn namespace(&self) -> Option<NamespaceConstraint<&Impl::NamespaceUrl>> {
- self.namespace.as_ref().map(|ns| match ns {
- NamespaceConstraint::Any => NamespaceConstraint::Any,
- NamespaceConstraint::Specific((_, ref url)) => NamespaceConstraint::Specific(url),
- })
- }
-}
-
-#[derive(Clone, Eq, PartialEq, ToShmem)]
-pub enum NamespaceConstraint<NamespaceUrl> {
- Any,
-
- /// Empty string for no namespace
- Specific(NamespaceUrl),
-}
-
-#[derive(Clone, Eq, PartialEq, ToShmem)]
-pub enum ParsedAttrSelectorOperation<AttrValue> {
- Exists,
- WithValue {
- operator: AttrSelectorOperator,
- case_sensitivity: ParsedCaseSensitivity,
- value: AttrValue,
- },
-}
-
-#[derive(Clone, Eq, PartialEq)]
-pub enum AttrSelectorOperation<AttrValue> {
- Exists,
- WithValue {
- operator: AttrSelectorOperator,
- case_sensitivity: CaseSensitivity,
- value: AttrValue,
- },
-}
-
-impl<AttrValue> AttrSelectorOperation<AttrValue> {
- pub fn eval_str(&self, element_attr_value: &str) -> bool
- where
- AttrValue: AsRef<str>,
- {
- match *self {
- AttrSelectorOperation::Exists => true,
- AttrSelectorOperation::WithValue {
- operator,
- case_sensitivity,
- ref value,
- } => operator.eval_str(
- element_attr_value,
- value.as_ref(),
- case_sensitivity,
- ),
- }
- }
-}
-
-#[derive(Clone, Copy, Eq, PartialEq, ToShmem)]
-pub enum AttrSelectorOperator {
- Equal,
- Includes,
- DashMatch,
- Prefix,
- Substring,
- Suffix,
-}
-
-impl ToCss for AttrSelectorOperator {
- fn to_css<W>(&self, dest: &mut W) -> fmt::Result
- where
- W: fmt::Write,
- {
- // https://drafts.csswg.org/cssom/#serializing-selectors
- // See "attribute selector".
- dest.write_str(match *self {
- AttrSelectorOperator::Equal => "=",
- AttrSelectorOperator::Includes => "~=",
- AttrSelectorOperator::DashMatch => "|=",
- AttrSelectorOperator::Prefix => "^=",
- AttrSelectorOperator::Substring => "*=",
- AttrSelectorOperator::Suffix => "$=",
- })
- }
-}
-
-impl AttrSelectorOperator {
- pub fn eval_str(
- self,
- element_attr_value: &str,
- attr_selector_value: &str,
- case_sensitivity: CaseSensitivity,
- ) -> bool {
- let e = element_attr_value.as_bytes();
- let s = attr_selector_value.as_bytes();
- let case = case_sensitivity;
- match self {
- AttrSelectorOperator::Equal => case.eq(e, s),
- AttrSelectorOperator::Prefix => {
- !s.is_empty() && e.len() >= s.len() && case.eq(&e[..s.len()], s)
- },
- AttrSelectorOperator::Suffix => {
- !s.is_empty() && e.len() >= s.len() && case.eq(&e[(e.len() - s.len())..], s)
- },
- AttrSelectorOperator::Substring => {
- !s.is_empty() && case.contains(element_attr_value, attr_selector_value)
- },
- AttrSelectorOperator::Includes => {
- !s.is_empty() &&
- element_attr_value
- .split(SELECTOR_WHITESPACE)
- .any(|part| case.eq(part.as_bytes(), s))
- },
- AttrSelectorOperator::DashMatch => {
- case.eq(e, s) || (e.get(s.len()) == Some(&b'-') && case.eq(&e[..s.len()], s))
- },
- }
- }
-}
-
-/// The definition of whitespace per CSS Selectors Level 3 § 4.
-pub static SELECTOR_WHITESPACE: &[char] = &[' ', '\t', '\n', '\r', '\x0C'];
-
-#[derive(Clone, Copy, Debug, Eq, PartialEq, ToShmem)]
-pub enum ParsedCaseSensitivity {
- /// 's' was specified.
- ExplicitCaseSensitive,
- /// 'i' was specified.
- AsciiCaseInsensitive,
- /// No flags were specified and HTML says this is a case-sensitive attribute.
- CaseSensitive,
- /// No flags were specified and HTML says this is a case-insensitive attribute.
- AsciiCaseInsensitiveIfInHtmlElementInHtmlDocument,
-}
-
-#[derive(Clone, Copy, Debug, Eq, PartialEq)]
-pub enum CaseSensitivity {
- CaseSensitive,
- AsciiCaseInsensitive,
-}
-
-impl CaseSensitivity {
- pub fn eq(self, a: &[u8], b: &[u8]) -> bool {
- match self {
- CaseSensitivity::CaseSensitive => a == b,
- CaseSensitivity::AsciiCaseInsensitive => a.eq_ignore_ascii_case(b),
- }
- }
-
- pub fn contains(self, haystack: &str, needle: &str) -> bool {
- match self {
- CaseSensitivity::CaseSensitive => haystack.contains(needle),
- CaseSensitivity::AsciiCaseInsensitive => {
- if let Some((&n_first_byte, n_rest)) = needle.as_bytes().split_first() {
- haystack.bytes().enumerate().any(|(i, byte)| {
- if !byte.eq_ignore_ascii_case(&n_first_byte) {
- return false;
- }
- let after_this_byte = &haystack.as_bytes()[i + 1..];
- match after_this_byte.get(..n_rest.len()) {
- None => false,
- Some(haystack_slice) => haystack_slice.eq_ignore_ascii_case(n_rest),
- }
- })
- } else {
- // any_str.contains("") == true,
- // though these cases should be handled with *NeverMatches and never go here.
- true
- }
- },
- }
- }
-}
diff --git a/components/selectors/bloom.rs b/components/selectors/bloom.rs
deleted file mode 100644
index 98461d1ba24..00000000000
--- a/components/selectors/bloom.rs
+++ /dev/null
@@ -1,422 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
-
-//! Counting and non-counting Bloom filters tuned for use as ancestor filters
-//! for selector matching.
-
-use std::fmt::{self, Debug};
-
-// The top 8 bits of the 32-bit hash value are not used by the bloom filter.
-// Consumers may rely on this to pack hashes more efficiently.
-pub const BLOOM_HASH_MASK: u32 = 0x00ffffff;
-const KEY_SIZE: usize = 12;
-
-const ARRAY_SIZE: usize = 1 << KEY_SIZE;
-const KEY_MASK: u32 = (1 << KEY_SIZE) - 1;
-
-/// A counting Bloom filter with 8-bit counters.
-pub type BloomFilter = CountingBloomFilter<BloomStorageU8>;
-
-/// A counting Bloom filter with parameterized storage to handle
-/// counters of different sizes. For now we assume that having two hash
-/// functions is enough, but we may revisit that decision later.
-///
-/// The filter uses an array with 2**KeySize entries.
-///
-/// Assuming a well-distributed hash function, a Bloom filter with
-/// array size M containing N elements and
-/// using k hash function has expected false positive rate exactly
-///
-/// $ (1 - (1 - 1/M)^{kN})^k $
-///
-/// because each array slot has a
-///
-/// $ (1 - 1/M)^{kN} $
-///
-/// chance of being 0, and the expected false positive rate is the
-/// probability that all of the k hash functions will hit a nonzero
-/// slot.
-///
-/// For reasonable assumptions (M large, kN large, which should both
-/// hold if we're worried about false positives) about M and kN this
-/// becomes approximately
-///
-/// $$ (1 - \exp(-kN/M))^k $$
-///
-/// For our special case of k == 2, that's $(1 - \exp(-2N/M))^2$,
-/// or in other words
-///
-/// $$ N/M = -0.5 * \ln(1 - \sqrt(r)) $$
-///
-/// where r is the false positive rate. This can be used to compute
-/// the desired KeySize for a given load N and false positive rate r.
-///
-/// If N/M is assumed small, then the false positive rate can
-/// further be approximated as 4*N^2/M^2. So increasing KeySize by
-/// 1, which doubles M, reduces the false positive rate by about a
-/// factor of 4, and a false positive rate of 1% corresponds to
-/// about M/N == 20.
-///
-/// What this means in practice is that for a few hundred keys using a
-/// KeySize of 12 gives false positive rates on the order of 0.25-4%.
-///
-/// Similarly, using a KeySize of 10 would lead to a 4% false
-/// positive rate for N == 100 and to quite bad false positive
-/// rates for larger N.
-#[derive(Clone, Default)]
-pub struct CountingBloomFilter<S>
-where
- S: BloomStorage,
-{
- storage: S,
-}
-
-impl<S> CountingBloomFilter<S>
-where
- S: BloomStorage,
-{
- /// Creates a new bloom filter.
- #[inline]
- pub fn new() -> Self {
- Default::default()
- }
-
- #[inline]
- pub fn clear(&mut self) {
- self.storage = Default::default();
- }
-
- // Slow linear accessor to make sure the bloom filter is zeroed. This should
- // never be used in release builds.
- #[cfg(debug_assertions)]
- pub fn is_zeroed(&self) -> bool {
- self.storage.is_zeroed()
- }
-
- #[cfg(not(debug_assertions))]
- pub fn is_zeroed(&self) -> bool {
- unreachable!()
- }
-
- /// Inserts an item with a particular hash into the bloom filter.
- #[inline]
- pub fn insert_hash(&mut self, hash: u32) {
- self.storage.adjust_first_slot(hash, true);
- self.storage.adjust_second_slot(hash, true);
- }
-
- /// Removes an item with a particular hash from the bloom filter.
- #[inline]
- pub fn remove_hash(&mut self, hash: u32) {
- self.storage.adjust_first_slot(hash, false);
- self.storage.adjust_second_slot(hash, false);
- }
-
- /// Check whether the filter might contain an item with the given hash.
- /// This can sometimes return true even if the item is not in the filter,
- /// but will never return false for items that are actually in the filter.
- #[inline]
- pub fn might_contain_hash(&self, hash: u32) -> bool {
- !self.storage.first_slot_is_empty(hash) && !self.storage.second_slot_is_empty(hash)
- }
-}
-
-impl<S> Debug for CountingBloomFilter<S>
-where
- S: BloomStorage,
-{
- fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
- let mut slots_used = 0;
- for i in 0..ARRAY_SIZE {
- if !self.storage.slot_is_empty(i) {
- slots_used += 1;
- }
- }
- write!(f, "BloomFilter({}/{})", slots_used, ARRAY_SIZE)
- }
-}
-
-pub trait BloomStorage: Clone + Default {
- fn slot_is_empty(&self, index: usize) -> bool;
- fn adjust_slot(&mut self, index: usize, increment: bool);
- fn is_zeroed(&self) -> bool;
-
- #[inline]
- fn first_slot_is_empty(&self, hash: u32) -> bool {
- self.slot_is_empty(Self::first_slot_index(hash))
- }
-
- #[inline]
- fn second_slot_is_empty(&self, hash: u32) -> bool {
- self.slot_is_empty(Self::second_slot_index(hash))
- }
-
- #[inline]
- fn adjust_first_slot(&mut self, hash: u32, increment: bool) {
- self.adjust_slot(Self::first_slot_index(hash), increment)
- }
-
- #[inline]
- fn adjust_second_slot(&mut self, hash: u32, increment: bool) {
- self.adjust_slot(Self::second_slot_index(hash), increment)
- }
-
- #[inline]
- fn first_slot_index(hash: u32) -> usize {
- hash1(hash) as usize
- }
-
- #[inline]
- fn second_slot_index(hash: u32) -> usize {
- hash2(hash) as usize
- }
-}
-
-/// Storage class for a CountingBloomFilter that has 8-bit counters.
-pub struct BloomStorageU8 {
- counters: [u8; ARRAY_SIZE],
-}
-
-impl BloomStorage for BloomStorageU8 {
- #[inline]
- fn adjust_slot(&mut self, index: usize, increment: bool) {
- let slot = &mut self.counters[index];
- if *slot != 0xff {
- // full
- if increment {
- *slot += 1;
- } else {
- *slot -= 1;
- }
- }
- }
-
- #[inline]
- fn slot_is_empty(&self, index: usize) -> bool {
- self.counters[index] == 0
- }
-
- #[inline]
- fn is_zeroed(&self) -> bool {
- self.counters.iter().all(|x| *x == 0)
- }
-}
-
-impl Default for BloomStorageU8 {
- fn default() -> Self {
- BloomStorageU8 {
- counters: [0; ARRAY_SIZE],
- }
- }
-}
-
-impl Clone for BloomStorageU8 {
- fn clone(&self) -> Self {
- BloomStorageU8 {
- counters: self.counters,
- }
- }
-}
-
-/// Storage class for a CountingBloomFilter that has 1-bit counters.
-pub struct BloomStorageBool {
- counters: [u8; ARRAY_SIZE / 8],
-}
-
-impl BloomStorage for BloomStorageBool {
- #[inline]
- fn adjust_slot(&mut self, index: usize, increment: bool) {
- let bit = 1 << (index % 8);
- let byte = &mut self.counters[index / 8];
-
- // Since we have only one bit for storage, decrementing it
- // should never do anything. Assert against an accidental
- // decrementing of a bit that was never set.
- assert!(
- increment || (*byte & bit) != 0,
- "should not decrement if slot is already false"
- );
-
- if increment {
- *byte |= bit;
- }
- }
-
- #[inline]
- fn slot_is_empty(&self, index: usize) -> bool {
- let bit = 1 << (index % 8);
- (self.counters[index / 8] & bit) == 0
- }
-
- #[inline]
- fn is_zeroed(&self) -> bool {
- self.counters.iter().all(|x| *x == 0)
- }
-}
-
-impl Default for BloomStorageBool {
- fn default() -> Self {
- BloomStorageBool {
- counters: [0; ARRAY_SIZE / 8],
- }
- }
-}
-
-impl Clone for BloomStorageBool {
- fn clone(&self) -> Self {
- BloomStorageBool {
- counters: self.counters,
- }
- }
-}
-
-#[inline]
-fn hash1(hash: u32) -> u32 {
- hash & KEY_MASK
-}
-
-#[inline]
-fn hash2(hash: u32) -> u32 {
- (hash >> KEY_SIZE) & KEY_MASK
-}
-
-#[test]
-fn create_and_insert_some_stuff() {
- use fxhash::FxHasher;
- use std::hash::{Hash, Hasher};
- use std::mem::transmute;
-
- fn hash_as_str(i: usize) -> u32 {
- let mut hasher = FxHasher::default();
- let s = i.to_string();
- s.hash(&mut hasher);
- let hash: u64 = hasher.finish();
- (hash >> 32) as u32 ^ (hash as u32)
- }
-
- let mut bf = BloomFilter::new();
-
- // Statically assert that ARRAY_SIZE is a multiple of 8, which
- // BloomStorageBool relies on.
- unsafe {
- transmute::<[u8; ARRAY_SIZE % 8], [u8; 0]>([]);
- }
-
- for i in 0_usize..1000 {
- bf.insert_hash(hash_as_str(i));
- }
-
- for i in 0_usize..1000 {
- assert!(bf.might_contain_hash(hash_as_str(i)));
- }
-
- let false_positives = (1001_usize..2000)
- .filter(|i| bf.might_contain_hash(hash_as_str(*i)))
- .count();
-
- assert!(false_positives < 190, "{} is not < 190", false_positives); // 19%.
-
- for i in 0_usize..100 {
- bf.remove_hash(hash_as_str(i));
- }
-
- for i in 100_usize..1000 {
- assert!(bf.might_contain_hash(hash_as_str(i)));
- }
-
- let false_positives = (0_usize..100)
- .filter(|i| bf.might_contain_hash(hash_as_str(*i)))
- .count();
-
- assert!(false_positives < 20, "{} is not < 20", false_positives); // 20%.
-
- bf.clear();
-
- for i in 0_usize..2000 {
- assert!(!bf.might_contain_hash(hash_as_str(i)));
- }
-}
-
-#[cfg(feature = "bench")]
-#[cfg(test)]
-mod bench {
- extern crate test;
- use super::BloomFilter;
-
- #[derive(Default)]
- struct HashGenerator(u32);
-
- impl HashGenerator {
- fn next(&mut self) -> u32 {
- // Each hash is split into two twelve-bit segments, which are used
- // as an index into an array. We increment each by 64 so that we
- // hit the next cache line, and then another 1 so that our wrapping
- // behavior leads us to different entries each time.
- //
- // Trying to simulate cold caches is rather difficult with the cargo
- // benchmarking setup, so it may all be moot depending on the number
- // of iterations that end up being run. But we might as well.
- self.0 += (65) + (65 << super::KEY_SIZE);
- self.0
- }
- }
-
- #[bench]
- fn create_insert_1000_remove_100_lookup_100(b: &mut test::Bencher) {
- b.iter(|| {
- let mut gen1 = HashGenerator::default();
- let mut gen2 = HashGenerator::default();
- let mut bf = BloomFilter::new();
- for _ in 0_usize..1000 {
- bf.insert_hash(gen1.next());
- }
- for _ in 0_usize..100 {
- bf.remove_hash(gen2.next());
- }
- for _ in 100_usize..200 {
- test::black_box(bf.might_contain_hash(gen2.next()));
- }
- });
- }
-
- #[bench]
- fn might_contain_10(b: &mut test::Bencher) {
- let bf = BloomFilter::new();
- let mut gen = HashGenerator::default();
- b.iter(|| {
- for _ in 0..10 {
- test::black_box(bf.might_contain_hash(gen.next()));
- }
- });
- }
-
- #[bench]
- fn clear(b: &mut test::Bencher) {
- let mut bf = Box::new(BloomFilter::new());
- b.iter(|| test::black_box(&mut bf).clear());
- }
-
- #[bench]
- fn insert_10(b: &mut test::Bencher) {
- let mut bf = BloomFilter::new();
- let mut gen = HashGenerator::default();
- b.iter(|| {
- for _ in 0..10 {
- test::black_box(bf.insert_hash(gen.next()));
- }
- });
- }
-
- #[bench]
- fn remove_10(b: &mut test::Bencher) {
- let mut bf = BloomFilter::new();
- let mut gen = HashGenerator::default();
- // Note: this will underflow, and that's ok.
- b.iter(|| {
- for _ in 0..10 {
- bf.remove_hash(gen.next())
- }
- });
- }
-}
diff --git a/components/selectors/build.rs b/components/selectors/build.rs
deleted file mode 100644
index c5c3803991e..00000000000
--- a/components/selectors/build.rs
+++ /dev/null
@@ -1,77 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
-
-extern crate phf_codegen;
-
-use std::env;
-use std::fs::File;
-use std::io::{BufWriter, Write};
-use std::path::Path;
-
-fn main() {
- let path = Path::new(&env::var_os("OUT_DIR").unwrap())
- .join("ascii_case_insensitive_html_attributes.rs");
- let mut file = BufWriter::new(File::create(&path).unwrap());
-
- let mut set = phf_codegen::Set::new();
- for name in ASCII_CASE_INSENSITIVE_HTML_ATTRIBUTES.split_whitespace() {
- set.entry(name);
- }
- write!(
- &mut file,
- "{{ static SET: ::phf::Set<&'static str> = {}; &SET }}",
- set.build(),
- )
- .unwrap();
-}
-
-/// <https://html.spec.whatwg.org/multipage/#selectors>
-static ASCII_CASE_INSENSITIVE_HTML_ATTRIBUTES: &str = r#"
- accept
- accept-charset
- align
- alink
- axis
- bgcolor
- charset
- checked
- clear
- codetype
- color
- compact
- declare
- defer
- dir
- direction
- disabled
- enctype
- face
- frame
- hreflang
- http-equiv
- lang
- language
- link
- media
- method
- multiple
- nohref
- noresize
- noshade
- nowrap
- readonly
- rel
- rev
- rules
- scope
- scrolling
- selected
- shape
- target
- text
- type
- valign
- valuetype
- vlink
-"#;
diff --git a/components/selectors/builder.rs b/components/selectors/builder.rs
deleted file mode 100644
index 63a6323c532..00000000000
--- a/components/selectors/builder.rs
+++ /dev/null
@@ -1,398 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
-
-//! Helper module to build up a selector safely and efficiently.
-//!
-//! Our selector representation is designed to optimize matching, and has
-//! several requirements:
-//! * All simple selectors and combinators are stored inline in the same buffer
-//! as Component instances.
-//! * We store the top-level compound selectors from right to left, i.e. in
-//! matching order.
-//! * We store the simple selectors for each combinator from left to right, so
-//! that we match the cheaper simple selectors first.
-//!
-//! Meeting all these constraints without extra memmove traffic during parsing
-//! is non-trivial. This module encapsulates those details and presents an
-//! easy-to-use API for the parser.
-
-use crate::parser::{Combinator, Component, RelativeSelector, Selector, SelectorImpl};
-use crate::sink::Push;
-use servo_arc::{Arc, HeaderWithLength, ThinArc};
-use smallvec::{self, SmallVec};
-use std::cmp;
-use std::iter;
-use std::ptr;
-use std::slice;
-
-/// Top-level SelectorBuilder struct. This should be stack-allocated by the
-/// consumer and never moved (because it contains a lot of inline data that
-/// would be slow to memmov).
-///
-/// After instantation, callers may call the push_simple_selector() and
-/// push_combinator() methods to append selector data as it is encountered
-/// (from left to right). Once the process is complete, callers should invoke
-/// build(), which transforms the contents of the SelectorBuilder into a heap-
-/// allocated Selector and leaves the builder in a drained state.
-#[derive(Debug)]
-pub struct SelectorBuilder<Impl: SelectorImpl> {
- /// The entire sequence of simple selectors, from left to right, without combinators.
- ///
- /// We make this large because the result of parsing a selector is fed into a new
- /// Arc-ed allocation, so any spilled vec would be a wasted allocation. Also,
- /// Components are large enough that we don't have much cache locality benefit
- /// from reserving stack space for fewer of them.
- simple_selectors: SmallVec<[Component<Impl>; 32]>,
- /// The combinators, and the length of the compound selector to their left.
- combinators: SmallVec<[(Combinator, usize); 16]>,
- /// The length of the current compount selector.
- current_len: usize,
-}
-
-impl<Impl: SelectorImpl> Default for SelectorBuilder<Impl> {
- #[inline(always)]
- fn default() -> Self {
- SelectorBuilder {
- simple_selectors: SmallVec::new(),
- combinators: SmallVec::new(),
- current_len: 0,
- }
- }
-}
-
-impl<Impl: SelectorImpl> Push<Component<Impl>> for SelectorBuilder<Impl> {
- fn push(&mut self, value: Component<Impl>) {
- self.push_simple_selector(value);
- }
-}
-
-impl<Impl: SelectorImpl> SelectorBuilder<Impl> {
- /// Pushes a simple selector onto the current compound selector.
- #[inline(always)]
- pub fn push_simple_selector(&mut self, ss: Component<Impl>) {
- assert!(!ss.is_combinator());
- self.simple_selectors.push(ss);
- self.current_len += 1;
- }
-
- /// Completes the current compound selector and starts a new one, delimited
- /// by the given combinator.
- #[inline(always)]
- pub fn push_combinator(&mut self, c: Combinator) {
- self.combinators.push((c, self.current_len));
- self.current_len = 0;
- }
-
- /// Returns true if combinators have ever been pushed to this builder.
- #[inline(always)]
- pub fn has_combinators(&self) -> bool {
- !self.combinators.is_empty()
- }
-
- /// Consumes the builder, producing a Selector.
- #[inline(always)]
- pub fn build(&mut self) -> ThinArc<SpecificityAndFlags, Component<Impl>> {
- // Compute the specificity and flags.
- let sf = specificity_and_flags(self.simple_selectors.iter());
- self.build_with_specificity_and_flags(sf)
- }
-
- /// Builds with an explicit SpecificityAndFlags. This is separated from build() so
- /// that unit tests can pass an explicit specificity.
- #[inline(always)]
- pub(crate) fn build_with_specificity_and_flags(
- &mut self,
- spec: SpecificityAndFlags,
- ) -> ThinArc<SpecificityAndFlags, Component<Impl>> {
- // First, compute the total number of Components we'll need to allocate
- // space for.
- let full_len = self.simple_selectors.len() + self.combinators.len();
-
- // Create the header.
- let header = HeaderWithLength::new(spec, full_len);
-
- // Create the Arc using an iterator that drains our buffers.
-
- // Use a raw pointer to be able to call set_len despite "borrowing" the slice.
- // This is similar to SmallVec::drain, but we use a slice here because
- // we’re gonna traverse it non-linearly.
- let raw_simple_selectors: *const [Component<Impl>] = &*self.simple_selectors;
- unsafe {
- // Panic-safety: if SelectorBuilderIter is not iterated to the end,
- // some simple selectors will safely leak.
- self.simple_selectors.set_len(0)
- }
- let (rest, current) = split_from_end(unsafe { &*raw_simple_selectors }, self.current_len);
- let iter = SelectorBuilderIter {
- current_simple_selectors: current.iter(),
- rest_of_simple_selectors: rest,
- combinators: self.combinators.drain(..).rev(),
- };
-
- Arc::into_thin(Arc::from_header_and_iter(header, iter))
- }
-}
-
-struct SelectorBuilderIter<'a, Impl: SelectorImpl> {
- current_simple_selectors: slice::Iter<'a, Component<Impl>>,
- rest_of_simple_selectors: &'a [Component<Impl>],
- combinators: iter::Rev<smallvec::Drain<'a, [(Combinator, usize); 16]>>,
-}
-
-impl<'a, Impl: SelectorImpl> ExactSizeIterator for SelectorBuilderIter<'a, Impl> {
- fn len(&self) -> usize {
- self.current_simple_selectors.len() +
- self.rest_of_simple_selectors.len() +
- self.combinators.len()
- }
-}
-
-impl<'a, Impl: SelectorImpl> Iterator for SelectorBuilderIter<'a, Impl> {
- type Item = Component<Impl>;
- #[inline(always)]
- fn next(&mut self) -> Option<Self::Item> {
- if let Some(simple_selector_ref) = self.current_simple_selectors.next() {
- // Move a simple selector out of this slice iterator.
- // This is safe because we’ve called SmallVec::set_len(0) above,
- // so SmallVec::drop won’t drop this simple selector.
- unsafe { Some(ptr::read(simple_selector_ref)) }
- } else {
- self.combinators.next().map(|(combinator, len)| {
- let (rest, current) = split_from_end(self.rest_of_simple_selectors, len);
- self.rest_of_simple_selectors = rest;
- self.current_simple_selectors = current.iter();
- Component::Combinator(combinator)
- })
- }
- }
-
- fn size_hint(&self) -> (usize, Option<usize>) {
- (self.len(), Some(self.len()))
- }
-}
-
-fn split_from_end<T>(s: &[T], at: usize) -> (&[T], &[T]) {
- s.split_at(s.len() - at)
-}
-
-bitflags! {
- /// Flags that indicate at which point of parsing a selector are we.
- #[derive(Default, ToShmem)]
- pub (crate) struct SelectorFlags : u8 {
- const HAS_PSEUDO = 1 << 0;
- const HAS_SLOTTED = 1 << 1;
- const HAS_PART = 1 << 2;
- const HAS_PARENT = 1 << 3;
- }
-}
-
-#[derive(Clone, Copy, Debug, Eq, PartialEq, ToShmem)]
-pub struct SpecificityAndFlags {
- /// There are two free bits here, since we use ten bits for each specificity
- /// kind (id, class, element).
- pub(crate) specificity: u32,
- /// There's padding after this field due to the size of the flags.
- pub(crate) flags: SelectorFlags,
-}
-
-impl SpecificityAndFlags {
- #[inline]
- pub fn specificity(&self) -> u32 {
- self.specificity
- }
-
- #[inline]
- pub fn has_pseudo_element(&self) -> bool {
- self.flags.intersects(SelectorFlags::HAS_PSEUDO)
- }
-
- #[inline]
- pub fn has_parent_selector(&self) -> bool {
- self.flags.intersects(SelectorFlags::HAS_PARENT)
- }
-
- #[inline]
- pub fn is_slotted(&self) -> bool {
- self.flags.intersects(SelectorFlags::HAS_SLOTTED)
- }
-
- #[inline]
- pub fn is_part(&self) -> bool {
- self.flags.intersects(SelectorFlags::HAS_PART)
- }
-}
-
-const MAX_10BIT: u32 = (1u32 << 10) - 1;
-
-#[derive(Add, AddAssign, Clone, Copy, Default, Eq, Ord, PartialEq, PartialOrd)]
-pub(crate) struct Specificity {
- id_selectors: u32,
- class_like_selectors: u32,
- element_selectors: u32,
-}
-
-impl From<u32> for Specificity {
- #[inline]
- fn from(value: u32) -> Specificity {
- assert!(value <= MAX_10BIT << 20 | MAX_10BIT << 10 | MAX_10BIT);
- Specificity {
- id_selectors: value >> 20,
- class_like_selectors: (value >> 10) & MAX_10BIT,
- element_selectors: value & MAX_10BIT,
- }
- }
-}
-
-impl From<Specificity> for u32 {
- #[inline]
- fn from(specificity: Specificity) -> u32 {
- cmp::min(specificity.id_selectors, MAX_10BIT) << 20 |
- cmp::min(specificity.class_like_selectors, MAX_10BIT) << 10 |
- cmp::min(specificity.element_selectors, MAX_10BIT)
- }
-}
-
-pub(crate) fn specificity_and_flags<Impl>(iter: slice::Iter<Component<Impl>>) -> SpecificityAndFlags
-where
- Impl: SelectorImpl,
-{
- complex_selector_specificity_and_flags(iter).into()
-}
-
-fn complex_selector_specificity_and_flags<Impl>(
- iter: slice::Iter<Component<Impl>>,
-) -> SpecificityAndFlags
-where
- Impl: SelectorImpl,
-{
- fn component_specificity<Impl>(
- simple_selector: &Component<Impl>,
- specificity: &mut Specificity,
- flags: &mut SelectorFlags,
- ) where
- Impl: SelectorImpl,
- {
- match *simple_selector {
- Component::Combinator(..) => {},
- Component::ParentSelector => flags.insert(SelectorFlags::HAS_PARENT),
- Component::Part(..) => {
- flags.insert(SelectorFlags::HAS_PART);
- specificity.element_selectors += 1
- },
- Component::PseudoElement(..) => {
- flags.insert(SelectorFlags::HAS_PSEUDO);
- specificity.element_selectors += 1
- },
- Component::LocalName(..) => specificity.element_selectors += 1,
- Component::Slotted(ref selector) => {
- flags.insert(SelectorFlags::HAS_SLOTTED);
- specificity.element_selectors += 1;
- // Note that due to the way ::slotted works we only compete with
- // other ::slotted rules, so the above rule doesn't really
- // matter, but we do it still for consistency with other
- // pseudo-elements.
- //
- // See: https://github.com/w3c/csswg-drafts/issues/1915
- *specificity += Specificity::from(selector.specificity());
- if selector.has_parent_selector() {
- flags.insert(SelectorFlags::HAS_PARENT);
- }
- },
- Component::Host(ref selector) => {
- specificity.class_like_selectors += 1;
- if let Some(ref selector) = *selector {
- // See: https://github.com/w3c/csswg-drafts/issues/1915
- *specificity += Specificity::from(selector.specificity());
- if selector.has_parent_selector() {
- flags.insert(SelectorFlags::HAS_PARENT);
- }
- }
- },
- Component::ID(..) => {
- specificity.id_selectors += 1;
- },
- Component::Class(..) |
- Component::AttributeInNoNamespace { .. } |
- Component::AttributeInNoNamespaceExists { .. } |
- Component::AttributeOther(..) |
- Component::Root |
- Component::Empty |
- Component::Scope |
- Component::Nth(..) |
- Component::NonTSPseudoClass(..) => {
- specificity.class_like_selectors += 1;
- },
- Component::NthOf(ref nth_of_data) => {
- // https://drafts.csswg.org/selectors/#specificity-rules:
- //
- // The specificity of the :nth-last-child() pseudo-class,
- // like the :nth-child() pseudo-class, combines the
- // specificity of a regular pseudo-class with that of its
- // selector argument S.
- specificity.class_like_selectors += 1;
- let sf = selector_list_specificity_and_flags(nth_of_data.selectors().iter());
- *specificity += Specificity::from(sf.specificity);
- flags.insert(sf.flags);
- },
- // https://drafts.csswg.org/selectors/#specificity-rules:
- //
- // The specificity of an :is(), :not(), or :has() pseudo-class
- // is replaced by the specificity of the most specific complex
- // selector in its selector list argument.
- Component::Where(ref list) |
- Component::Negation(ref list) |
- Component::Is(ref list) => {
- let sf = selector_list_specificity_and_flags(list.iter());
- if !matches!(*simple_selector, Component::Where(..)) {
- *specificity += Specificity::from(sf.specificity);
- }
- flags.insert(sf.flags);
- },
- Component::Has(ref relative_selectors) => {
- let sf = relative_selector_list_specificity_and_flags(relative_selectors);
- *specificity += Specificity::from(sf.specificity);
- flags.insert(sf.flags);
- },
- Component::ExplicitUniversalType |
- Component::ExplicitAnyNamespace |
- Component::ExplicitNoNamespace |
- Component::DefaultNamespace(..) |
- Component::Namespace(..) |
- Component::RelativeSelectorAnchor => {
- // Does not affect specificity
- },
- }
- }
-
- let mut specificity = Default::default();
- let mut flags = Default::default();
- for simple_selector in iter {
- component_specificity(&simple_selector, &mut specificity, &mut flags);
- }
- SpecificityAndFlags {
- specificity: specificity.into(),
- flags,
- }
-}
-
-/// Finds the maximum specificity of elements in the list and returns it.
-pub(crate) fn selector_list_specificity_and_flags<'a, Impl: SelectorImpl>(
- itr: impl Iterator<Item = &'a Selector<Impl>>,
-) -> SpecificityAndFlags {
- let mut specificity = 0;
- let mut flags = SelectorFlags::empty();
- for selector in itr {
- specificity = std::cmp::max(specificity, selector.specificity());
- if selector.has_parent_selector() {
- flags.insert(SelectorFlags::HAS_PARENT);
- }
- }
- SpecificityAndFlags { specificity, flags }
-}
-
-pub(crate) fn relative_selector_list_specificity_and_flags<Impl: SelectorImpl>(
- list: &[RelativeSelector<Impl>],
-) -> SpecificityAndFlags {
- selector_list_specificity_and_flags(list.iter().map(|rel| &rel.selector))
-}
diff --git a/components/selectors/context.rs b/components/selectors/context.rs
deleted file mode 100644
index d8c461660d3..00000000000
--- a/components/selectors/context.rs
+++ /dev/null
@@ -1,363 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
-
-use crate::attr::CaseSensitivity;
-use crate::bloom::BloomFilter;
-use crate::nth_index_cache::{NthIndexCache, NthIndexCacheInner};
-use crate::parser::{Selector, SelectorImpl};
-use crate::tree::{Element, OpaqueElement};
-
-/// What kind of selector matching mode we should use.
-///
-/// There are two modes of selector matching. The difference is only noticeable
-/// in presence of pseudo-elements.
-#[derive(Clone, Copy, Debug, PartialEq)]
-pub enum MatchingMode {
- /// Don't ignore any pseudo-element selectors.
- Normal,
-
- /// Ignores any stateless pseudo-element selectors in the rightmost sequence
- /// of simple selectors.
- ///
- /// This is useful, for example, to match against ::before when you aren't a
- /// pseudo-element yourself.
- ///
- /// For example, in presence of `::before:hover`, it would never match, but
- /// `::before` would be ignored as in "matching".
- ///
- /// It's required for all the selectors you match using this mode to have a
- /// pseudo-element.
- ForStatelessPseudoElement,
-}
-
-/// The mode to use when matching unvisited and visited links.
-#[derive(Clone, Copy, Debug, Eq, PartialEq)]
-pub enum VisitedHandlingMode {
- /// All links are matched as if they are unvisted.
- AllLinksUnvisited,
- /// All links are matched as if they are visited and unvisited (both :link
- /// and :visited match).
- ///
- /// This is intended to be used from invalidation code, to be conservative
- /// about whether we need to restyle a link.
- AllLinksVisitedAndUnvisited,
- /// A element's "relevant link" is the element being matched if it is a link
- /// or the nearest ancestor link. The relevant link is matched as though it
- /// is visited, and all other links are matched as if they are unvisited.
- RelevantLinkVisited,
-}
-
-impl VisitedHandlingMode {
- #[inline]
- pub fn matches_visited(&self) -> bool {
- matches!(
- *self,
- VisitedHandlingMode::RelevantLinkVisited |
- VisitedHandlingMode::AllLinksVisitedAndUnvisited
- )
- }
-
- #[inline]
- pub fn matches_unvisited(&self) -> bool {
- matches!(
- *self,
- VisitedHandlingMode::AllLinksUnvisited |
- VisitedHandlingMode::AllLinksVisitedAndUnvisited
- )
- }
-}
-
-/// Whether we need to set selector invalidation flags on elements for this
-/// match request.
-#[derive(Clone, Copy, Debug, PartialEq)]
-pub enum NeedsSelectorFlags {
- No,
- Yes,
-}
-
-/// Which quirks mode is this document in.
-///
-/// See: https://quirks.spec.whatwg.org/
-#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
-pub enum QuirksMode {
- /// Quirks mode.
- Quirks,
- /// Limited quirks mode.
- LimitedQuirks,
- /// No quirks mode.
- NoQuirks,
-}
-
-impl QuirksMode {
- #[inline]
- pub fn classes_and_ids_case_sensitivity(self) -> CaseSensitivity {
- match self {
- QuirksMode::NoQuirks | QuirksMode::LimitedQuirks => CaseSensitivity::CaseSensitive,
- QuirksMode::Quirks => CaseSensitivity::AsciiCaseInsensitive,
- }
- }
-}
-
-/// Whether or not this matching considered relative selector.
-#[derive(Clone, Copy, Debug, PartialEq)]
-pub enum RelativeSelectorMatchingState {
- /// Was not considered for any relative selector.
- None,
- /// Relative selector was considered for a match, but the element to be
- /// under matching would not anchor the relative selector. i.e. The
- /// relative selector was not part of the first compound selector (in match
- /// order).
- Considered,
- /// Same as above, but the relative selector was part of the first compound
- /// selector (in match order).
- ConsideredAnchor,
-}
-
-/// Data associated with the matching process for a element. This context is
-/// used across many selectors for an element, so it's not appropriate for
-/// transient data that applies to only a single selector.
-pub struct MatchingContext<'a, Impl>
-where
- Impl: SelectorImpl,
-{
- /// Input with the matching mode we should use when matching selectors.
- matching_mode: MatchingMode,
- /// Input with the bloom filter used to fast-reject selectors.
- pub bloom_filter: Option<&'a BloomFilter>,
- /// A cache to speed up nth-index-like selectors.
- pub nth_index_cache: &'a mut NthIndexCache,
- /// The element which is going to match :scope pseudo-class. It can be
- /// either one :scope element, or the scoping element.
- ///
- /// Note that, although in theory there can be multiple :scope elements,
- /// in current specs, at most one is specified, and when there is one,
- /// scoping element is not relevant anymore, so we use a single field for
- /// them.
- ///
- /// When this is None, :scope will match the root element.
- ///
- /// See https://drafts.csswg.org/selectors-4/#scope-pseudo
- pub scope_element: Option<OpaqueElement>,
-
- /// The current shadow host we're collecting :host rules for.
- pub current_host: Option<OpaqueElement>,
-
- /// Controls how matching for links is handled.
- visited_handling: VisitedHandlingMode,
-
- /// The current nesting level of selectors that we're matching.
- nesting_level: usize,
-
- /// Whether we're inside a negation or not.
- in_negation: bool,
-
- /// An optional hook function for checking whether a pseudo-element
- /// should match when matching_mode is ForStatelessPseudoElement.
- pub pseudo_element_matching_fn: Option<&'a dyn Fn(&Impl::PseudoElement) -> bool>,
-
- /// Extra implementation-dependent matching data.
- pub extra_data: Impl::ExtraMatchingData<'a>,
-
- /// The current element we're anchoring on for evaluating the relative selector.
- current_relative_selector_anchor: Option<OpaqueElement>,
- pub considered_relative_selector: RelativeSelectorMatchingState,
-
- quirks_mode: QuirksMode,
- needs_selector_flags: NeedsSelectorFlags,
- classes_and_ids_case_sensitivity: CaseSensitivity,
- _impl: ::std::marker::PhantomData<Impl>,
-}
-
-impl<'a, Impl> MatchingContext<'a, Impl>
-where
- Impl: SelectorImpl,
-{
- /// Constructs a new `MatchingContext`.
- pub fn new(
- matching_mode: MatchingMode,
- bloom_filter: Option<&'a BloomFilter>,
- nth_index_cache: &'a mut NthIndexCache,
- quirks_mode: QuirksMode,
- needs_selector_flags: NeedsSelectorFlags,
- ) -> Self {
- Self::new_for_visited(
- matching_mode,
- bloom_filter,
- nth_index_cache,
- VisitedHandlingMode::AllLinksUnvisited,
- quirks_mode,
- needs_selector_flags,
- )
- }
-
- /// Constructs a new `MatchingContext` for use in visited matching.
- pub fn new_for_visited(
- matching_mode: MatchingMode,
- bloom_filter: Option<&'a BloomFilter>,
- nth_index_cache: &'a mut NthIndexCache,
- visited_handling: VisitedHandlingMode,
- quirks_mode: QuirksMode,
- needs_selector_flags: NeedsSelectorFlags,
- ) -> Self {
- Self {
- matching_mode,
- bloom_filter,
- visited_handling,
- nth_index_cache,
- quirks_mode,
- classes_and_ids_case_sensitivity: quirks_mode.classes_and_ids_case_sensitivity(),
- needs_selector_flags,
- scope_element: None,
- current_host: None,
- nesting_level: 0,
- in_negation: false,
- pseudo_element_matching_fn: None,
- extra_data: Default::default(),
- current_relative_selector_anchor: None,
- considered_relative_selector: RelativeSelectorMatchingState::None,
- _impl: ::std::marker::PhantomData,
- }
- }
-
- // Grab a reference to the appropriate cache.
- #[inline]
- pub fn nth_index_cache(
- &mut self,
- is_of_type: bool,
- is_from_end: bool,
- selectors: &[Selector<Impl>],
- ) -> &mut NthIndexCacheInner {
- self.nth_index_cache.get(is_of_type, is_from_end, selectors)
- }
-
- /// Whether we're matching a nested selector.
- #[inline]
- pub fn is_nested(&self) -> bool {
- self.nesting_level != 0
- }
-
- /// Whether we're matching inside a :not(..) selector.
- #[inline]
- pub fn in_negation(&self) -> bool {
- self.in_negation
- }
-
- /// The quirks mode of the document.
- #[inline]
- pub fn quirks_mode(&self) -> QuirksMode {
- self.quirks_mode
- }
-
- /// The matching-mode for this selector-matching operation.
- #[inline]
- pub fn matching_mode(&self) -> MatchingMode {
- self.matching_mode
- }
-
- /// Whether we need to set selector flags.
- #[inline]
- pub fn needs_selector_flags(&self) -> bool {
- self.needs_selector_flags == NeedsSelectorFlags::Yes
- }
-
- /// The case-sensitivity for class and ID selectors
- #[inline]
- pub fn classes_and_ids_case_sensitivity(&self) -> CaseSensitivity {
- self.classes_and_ids_case_sensitivity
- }
-
- /// Runs F with a deeper nesting level.
- #[inline]
- pub fn nest<F, R>(&mut self, f: F) -> R
- where
- F: FnOnce(&mut Self) -> R,
- {
- self.nesting_level += 1;
- let result = f(self);
- self.nesting_level -= 1;
- result
- }
-
- /// Runs F with a deeper nesting level, and marking ourselves in a negation,
- /// for a :not(..) selector, for example.
- #[inline]
- pub fn nest_for_negation<F, R>(&mut self, f: F) -> R
- where
- F: FnOnce(&mut Self) -> R,
- {
- let old_in_negation = self.in_negation;
- self.in_negation = true;
- let result = self.nest(f);
- self.in_negation = old_in_negation;
- result
- }
-
- #[inline]
- pub fn visited_handling(&self) -> VisitedHandlingMode {
- self.visited_handling
- }
-
- /// Runs F with a different VisitedHandlingMode.
- #[inline]
- pub fn with_visited_handling_mode<F, R>(
- &mut self,
- handling_mode: VisitedHandlingMode,
- f: F,
- ) -> R
- where
- F: FnOnce(&mut Self) -> R,
- {
- let original_handling_mode = self.visited_handling;
- self.visited_handling = handling_mode;
- let result = f(self);
- self.visited_handling = original_handling_mode;
- result
- }
-
- /// Runs F with a given shadow host which is the root of the tree whose
- /// rules we're matching.
- #[inline]
- pub fn with_shadow_host<F, E, R>(&mut self, host: Option<E>, f: F) -> R
- where
- E: Element,
- F: FnOnce(&mut Self) -> R,
- {
- let original_host = self.current_host.take();
- self.current_host = host.map(|h| h.opaque());
- let result = f(self);
- self.current_host = original_host;
- result
- }
-
- /// Returns the current shadow host whose shadow root we're matching rules
- /// against.
- #[inline]
- pub fn shadow_host(&self) -> Option<OpaqueElement> {
- self.current_host
- }
-
- /// Runs F with a deeper nesting level, with the given element as the anchor,
- /// for a :has(...) selector, for example.
- #[inline]
- pub fn nest_for_relative_selector<F, R>(&mut self, anchor: OpaqueElement, f: F) -> R
- where
- F: FnOnce(&mut Self) -> R,
- {
- debug_assert!(
- self.current_relative_selector_anchor.is_none(),
- "Nesting should've been rejected at parse time"
- );
- self.current_relative_selector_anchor = Some(anchor);
- self.considered_relative_selector = RelativeSelectorMatchingState::Considered;
- let result = self.nest(f);
- self.current_relative_selector_anchor = None;
- result
- }
-
- /// Returns the current anchor element to evaluate the relative selector against.
- #[inline]
- pub fn relative_selector_anchor(&self) -> Option<OpaqueElement> {
- self.current_relative_selector_anchor
- }
-}
diff --git a/components/selectors/lib.rs b/components/selectors/lib.rs
deleted file mode 100644
index d71d3c84876..00000000000
--- a/components/selectors/lib.rs
+++ /dev/null
@@ -1,40 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
-
-// Make |cargo bench| work.
-#![cfg_attr(feature = "bench", feature(test))]
-
-#[macro_use]
-extern crate bitflags;
-#[macro_use]
-extern crate cssparser;
-#[macro_use]
-extern crate debug_unreachable;
-#[macro_use]
-extern crate derive_more;
-extern crate fxhash;
-#[macro_use]
-extern crate log;
-extern crate phf;
-extern crate precomputed_hash;
-extern crate servo_arc;
-extern crate smallvec;
-extern crate to_shmem;
-#[macro_use]
-extern crate to_shmem_derive;
-
-pub mod attr;
-pub mod bloom;
-mod builder;
-pub mod context;
-pub mod matching;
-mod nth_index_cache;
-pub mod parser;
-pub mod sink;
-mod tree;
-pub mod visitor;
-
-pub use crate::nth_index_cache::NthIndexCache;
-pub use crate::parser::{Parser, SelectorImpl, SelectorList};
-pub use crate::tree::{Element, OpaqueElement};
diff --git a/components/selectors/matching.rs b/components/selectors/matching.rs
deleted file mode 100644
index 83bc878797a..00000000000
--- a/components/selectors/matching.rs
+++ /dev/null
@@ -1,1133 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
-
-use crate::attr::{
- AttrSelectorOperation, CaseSensitivity, NamespaceConstraint, ParsedAttrSelectorOperation,
- ParsedCaseSensitivity,
-};
-use crate::bloom::{BloomFilter, BLOOM_HASH_MASK};
-use crate::parser::{
- AncestorHashes, Combinator, Component, LocalName, NthSelectorData, RelativeSelectorMatchHint,
-};
-use crate::parser::{
- NonTSPseudoClass, RelativeSelector, Selector, SelectorImpl, SelectorIter, SelectorList,
-};
-use crate::tree::Element;
-use bitflags::bitflags;
-use smallvec::SmallVec;
-use std::borrow::Borrow;
-use std::iter;
-
-pub use crate::context::*;
-
-// The bloom filter for descendant CSS selectors will have a <1% false
-// positive rate until it has this many selectors in it, then it will
-// rapidly increase.
-pub static RECOMMENDED_SELECTOR_BLOOM_FILTER_SIZE: usize = 4096;
-
-bitflags! {
- /// Set of flags that are set on either the element or its parent (depending
- /// on the flag) if the element could potentially match a selector.
- pub struct ElementSelectorFlags: usize {
- /// When a child is added or removed from the parent, all the children
- /// must be restyled, because they may match :nth-last-child,
- /// :last-of-type, :nth-last-of-type, or :only-of-type.
- const HAS_SLOW_SELECTOR = 1 << 0;
-
- /// When a child is added or removed from the parent, any later
- /// children must be restyled, because they may match :nth-child,
- /// :first-of-type, or :nth-of-type.
- const HAS_SLOW_SELECTOR_LATER_SIBLINGS = 1 << 1;
-
- /// When a DOM mutation occurs on a child that might be matched by
- /// :nth-last-child(.. of <selector list>), earlier children must be
- /// restyled, and HAS_SLOW_SELECTOR will be set (which normally
- /// indicates that all children will be restyled).
- ///
- /// Similarly, when a DOM mutation occurs on a child that might be
- /// matched by :nth-child(.. of <selector list>), later children must be
- /// restyled, and HAS_SLOW_SELECTOR_LATER_SIBLINGS will be set.
- const HAS_SLOW_SELECTOR_NTH_OF = 1 << 2;
-
- /// When a child is added or removed from the parent, the first and
- /// last children must be restyled, because they may match :first-child,
- /// :last-child, or :only-child.
- const HAS_EDGE_CHILD_SELECTOR = 1 << 3;
-
- /// The element has an empty selector, so when a child is appended we
- /// might need to restyle the parent completely.
- const HAS_EMPTY_SELECTOR = 1 << 4;
- }
-}
-
-impl ElementSelectorFlags {
- /// Returns the subset of flags that apply to the element.
- pub fn for_self(self) -> ElementSelectorFlags {
- self & ElementSelectorFlags::HAS_EMPTY_SELECTOR
- }
-
- /// Returns the subset of flags that apply to the parent.
- pub fn for_parent(self) -> ElementSelectorFlags {
- self & (ElementSelectorFlags::HAS_SLOW_SELECTOR |
- ElementSelectorFlags::HAS_SLOW_SELECTOR_LATER_SIBLINGS |
- ElementSelectorFlags::HAS_SLOW_SELECTOR_NTH_OF |
- ElementSelectorFlags::HAS_EDGE_CHILD_SELECTOR)
- }
-}
-
-/// Holds per-compound-selector data.
-struct LocalMatchingContext<'a, 'b: 'a, Impl: SelectorImpl> {
- shared: &'a mut MatchingContext<'b, Impl>,
- quirks_data: Option<(Rightmost, SelectorIter<'a, Impl>)>,
-}
-
-#[inline(always)]
-pub fn matches_selector_list<E>(
- selector_list: &SelectorList<E::Impl>,
- element: &E,
- context: &mut MatchingContext<E::Impl>,
-) -> bool
-where
- E: Element,
-{
- // This is pretty much any(..) but manually inlined because the compiler
- // refuses to do so from querySelector / querySelectorAll.
- for selector in &selector_list.0 {
- let matches = matches_selector(selector, 0, None, element, context);
- if matches {
- return true;
- }
- }
-
- false
-}
-
-#[inline(always)]
-fn may_match(hashes: &AncestorHashes, bf: &BloomFilter) -> bool {
- // Check the first three hashes. Note that we can check for zero before
- // masking off the high bits, since if any of the first three hashes is
- // zero the fourth will be as well. We also take care to avoid the
- // special-case complexity of the fourth hash until we actually reach it,
- // because we usually don't.
- //
- // To be clear: this is all extremely hot.
- for i in 0..3 {
- let packed = hashes.packed_hashes[i];
- if packed == 0 {
- // No more hashes left - unable to fast-reject.
- return true;
- }
-
- if !bf.might_contain_hash(packed & BLOOM_HASH_MASK) {
- // Hooray! We fast-rejected on this hash.
- return false;
- }
- }
-
- // Now do the slighty-more-complex work of synthesizing the fourth hash,
- // and check it against the filter if it exists.
- let fourth = hashes.fourth_hash();
- fourth == 0 || bf.might_contain_hash(fourth)
-}
-
-/// A result of selector matching, includes 3 failure types,
-///
-/// NotMatchedAndRestartFromClosestLaterSibling
-/// NotMatchedAndRestartFromClosestDescendant
-/// NotMatchedGlobally
-///
-/// When NotMatchedGlobally appears, stop selector matching completely since
-/// the succeeding selectors never matches.
-/// It is raised when
-/// Child combinator cannot find the candidate element.
-/// Descendant combinator cannot find the candidate element.
-///
-/// When NotMatchedAndRestartFromClosestDescendant appears, the selector
-/// matching does backtracking and restarts from the closest Descendant
-/// combinator.
-/// It is raised when
-/// NextSibling combinator cannot find the candidate element.
-/// LaterSibling combinator cannot find the candidate element.
-/// Child combinator doesn't match on the found element.
-///
-/// When NotMatchedAndRestartFromClosestLaterSibling appears, the selector
-/// matching does backtracking and restarts from the closest LaterSibling
-/// combinator.
-/// It is raised when
-/// NextSibling combinator doesn't match on the found element.
-///
-/// For example, when the selector "d1 d2 a" is provided and we cannot *find*
-/// an appropriate ancestor element for "d1", this selector matching raises
-/// NotMatchedGlobally since even if "d2" is moved to more upper element, the
-/// candidates for "d1" becomes less than before and d1 .
-///
-/// The next example is siblings. When the selector "b1 + b2 ~ d1 a" is
-/// provided and we cannot *find* an appropriate brother element for b1,
-/// the selector matching raises NotMatchedAndRestartFromClosestDescendant.
-/// The selectors ("b1 + b2 ~") doesn't match and matching restart from "d1".
-///
-/// The additional example is child and sibling. When the selector
-/// "b1 + c1 > b2 ~ d1 a" is provided and the selector "b1" doesn't match on
-/// the element, this "b1" raises NotMatchedAndRestartFromClosestLaterSibling.
-/// However since the selector "c1" raises
-/// NotMatchedAndRestartFromClosestDescendant. So the selector
-/// "b1 + c1 > b2 ~ " doesn't match and restart matching from "d1".
-#[derive(Clone, Copy, Eq, PartialEq)]
-enum SelectorMatchingResult {
- Matched,
- NotMatchedAndRestartFromClosestLaterSibling,
- NotMatchedAndRestartFromClosestDescendant,
- NotMatchedGlobally,
-}
-
-/// Matches a selector, fast-rejecting against a bloom filter.
-///
-/// We accept an offset to allow consumers to represent and match against
-/// partial selectors (indexed from the right). We use this API design, rather
-/// than having the callers pass a SelectorIter, because creating a SelectorIter
-/// requires dereferencing the selector to get the length, which adds an
-/// unncessary cache miss for cases when we can fast-reject with AncestorHashes
-/// (which the caller can store inline with the selector pointer).
-#[inline(always)]
-pub fn matches_selector<E>(
- selector: &Selector<E::Impl>,
- offset: usize,
- hashes: Option<&AncestorHashes>,
- element: &E,
- context: &mut MatchingContext<E::Impl>,
-) -> bool
-where
- E: Element,
-{
- // Use the bloom filter to fast-reject.
- if let Some(hashes) = hashes {
- if let Some(filter) = context.bloom_filter {
- if !may_match(hashes, filter) {
- return false;
- }
- }
- }
-
- matches_complex_selector(selector.iter_from(offset), element, context)
-}
-
-/// Whether a compound selector matched, and whether it was the rightmost
-/// selector inside the complex selector.
-pub enum CompoundSelectorMatchingResult {
- /// The selector was fully matched.
- FullyMatched,
- /// The compound selector matched, and the next combinator offset is
- /// `next_combinator_offset`.
- Matched { next_combinator_offset: usize },
- /// The selector didn't match.
- NotMatched,
-}
-
-/// Matches a compound selector belonging to `selector`, starting at offset
-/// `from_offset`, matching left to right.
-///
-/// Requires that `from_offset` points to a `Combinator`.
-///
-/// NOTE(emilio): This doesn't allow to match in the leftmost sequence of the
-/// complex selector, but it happens to be the case we don't need it.
-pub fn matches_compound_selector_from<E>(
- selector: &Selector<E::Impl>,
- mut from_offset: usize,
- context: &mut MatchingContext<E::Impl>,
- element: &E,
-) -> CompoundSelectorMatchingResult
-where
- E: Element,
-{
- if cfg!(debug_assertions) && from_offset != 0 {
- selector.combinator_at_parse_order(from_offset - 1); // This asserts.
- }
-
- let mut local_context = LocalMatchingContext {
- shared: context,
- quirks_data: None,
- };
-
- // Find the end of the selector or the next combinator, then match
- // backwards, so that we match in the same order as
- // matches_complex_selector, which is usually faster.
- let start_offset = from_offset;
- for component in selector.iter_raw_parse_order_from(from_offset) {
- if matches!(*component, Component::Combinator(..)) {
- debug_assert_ne!(from_offset, 0, "Selector started with a combinator?");
- break;
- }
-
- from_offset += 1;
- }
-
- debug_assert!(from_offset >= 1);
- debug_assert!(from_offset <= selector.len());
-
- let iter = selector.iter_from(selector.len() - from_offset);
- debug_assert!(
- iter.clone().next().is_some() ||
- (from_offset != selector.len() &&
- matches!(
- selector.combinator_at_parse_order(from_offset),
- Combinator::SlotAssignment | Combinator::PseudoElement
- )),
- "Got the math wrong: {:?} | {:?} | {} {}",
- selector,
- selector.iter_raw_match_order().as_slice(),
- from_offset,
- start_offset
- );
-
- for component in iter {
- if !matches_simple_selector(component, element, &mut local_context) {
- return CompoundSelectorMatchingResult::NotMatched;
- }
- }
-
- if from_offset != selector.len() {
- return CompoundSelectorMatchingResult::Matched {
- next_combinator_offset: from_offset,
- };
- }
-
- CompoundSelectorMatchingResult::FullyMatched
-}
-
-/// Matches a complex selector.
-#[inline(always)]
-pub fn matches_complex_selector<E>(
- mut iter: SelectorIter<E::Impl>,
- element: &E,
- context: &mut MatchingContext<E::Impl>,
-) -> bool
-where
- E: Element,
-{
- // If this is the special pseudo-element mode, consume the ::pseudo-element
- // before proceeding, since the caller has already handled that part.
- if context.matching_mode() == MatchingMode::ForStatelessPseudoElement && !context.is_nested() {
- // Consume the pseudo.
- match *iter.next().unwrap() {
- Component::PseudoElement(ref pseudo) => {
- if let Some(ref f) = context.pseudo_element_matching_fn {
- if !f(pseudo) {
- return false;
- }
- }
- },
- ref other => {
- debug_assert!(
- false,
- "Used MatchingMode::ForStatelessPseudoElement \
- in a non-pseudo selector {:?}",
- other
- );
- return false;
- },
- }
-
- if !iter.matches_for_stateless_pseudo_element() {
- return false;
- }
-
- // Advance to the non-pseudo-element part of the selector.
- let next_sequence = iter.next_sequence().unwrap();
- debug_assert_eq!(next_sequence, Combinator::PseudoElement);
- }
-
- let result = matches_complex_selector_internal(iter, element, context, Rightmost::Yes);
-
- matches!(result, SelectorMatchingResult::Matched)
-}
-
-/// Matches each selector of a list as a complex selector
-#[inline(always)]
-pub fn list_matches_complex_selector<E: Element>(
- list: &[Selector<E::Impl>],
- element: &E,
- context: &mut MatchingContext<E::Impl>,
-) -> bool {
- for selector in list {
- if matches_complex_selector(selector.iter(), element, context) {
- return true;
- }
- }
- false
-}
-
-/// Matches a relative selector in a list of relative selectors.
-fn matches_relative_selectors<E: Element>(
- selectors: &[RelativeSelector<E::Impl>],
- element: &E,
- context: &mut MatchingContext<E::Impl>,
-) -> bool {
- // If we've considered anchoring `:has()` selector while trying to match this element,
- // mark it as such, as it has implications on style sharing (See style sharing
- // code for further information).
- context.considered_relative_selector = RelativeSelectorMatchingState::ConsideredAnchor;
- for RelativeSelector {
- match_hint,
- selector,
- } in selectors.iter()
- {
- let (traverse_subtree, traverse_siblings, mut next_element) = match match_hint {
- RelativeSelectorMatchHint::InChild => (false, true, element.first_element_child()),
- RelativeSelectorMatchHint::InSubtree => (true, true, element.first_element_child()),
- RelativeSelectorMatchHint::InSibling => (false, true, element.next_sibling_element()),
- RelativeSelectorMatchHint::InSiblingSubtree => {
- (true, true, element.next_sibling_element())
- },
- RelativeSelectorMatchHint::InNextSibling => {
- (false, false, element.next_sibling_element())
- },
- RelativeSelectorMatchHint::InNextSiblingSubtree => {
- (true, false, element.next_sibling_element())
- },
- };
- while let Some(el) = next_element {
- // TODO(dshin): `:has()` matching can get expensive when determining style changes.
- // We'll need caching/filtering here, which is tracked in bug 1822177.
- if matches_complex_selector(selector.iter(), &el, context) {
- return true;
- }
- if traverse_subtree && matches_relative_selector_subtree(selector, &el, context) {
- return true;
- }
- if !traverse_siblings {
- break;
- }
- next_element = el.next_sibling_element();
- }
- }
-
- false
-}
-
-fn matches_relative_selector_subtree<E: Element>(
- selector: &Selector<E::Impl>,
- element: &E,
- context: &mut MatchingContext<E::Impl>,
-) -> bool {
- let mut current = element.first_element_child();
-
- while let Some(el) = current {
- if matches_complex_selector(selector.iter(), &el, context) {
- return true;
- }
-
- if matches_relative_selector_subtree(selector, &el, context) {
- return true;
- }
-
- current = el.next_sibling_element();
- }
-
- false
-}
-
-/// Whether the :hover and :active quirk applies.
-///
-/// https://quirks.spec.whatwg.org/#the-active-and-hover-quirk
-fn hover_and_active_quirk_applies<Impl: SelectorImpl>(
- selector_iter: &SelectorIter<Impl>,
- context: &MatchingContext<Impl>,
- rightmost: Rightmost,
-) -> bool {
- if context.quirks_mode() != QuirksMode::Quirks {
- return false;
- }
-
- if context.is_nested() {
- return false;
- }
-
- // This compound selector had a pseudo-element to the right that we
- // intentionally skipped.
- if rightmost == Rightmost::Yes &&
- context.matching_mode() == MatchingMode::ForStatelessPseudoElement
- {
- return false;
- }
-
- selector_iter.clone().all(|simple| match *simple {
- Component::LocalName(_) |
- Component::AttributeInNoNamespaceExists { .. } |
- Component::AttributeInNoNamespace { .. } |
- Component::AttributeOther(_) |
- Component::ID(_) |
- Component::Class(_) |
- Component::PseudoElement(_) |
- Component::Negation(_) |
- Component::Empty |
- Component::Nth(_) |
- Component::NthOf(_) => false,
- Component::NonTSPseudoClass(ref pseudo_class) => pseudo_class.is_active_or_hover(),
- _ => true,
- })
-}
-
-#[derive(Clone, Copy, PartialEq)]
-enum Rightmost {
- Yes,
- No,
-}
-
-#[inline(always)]
-fn next_element_for_combinator<E>(
- element: &E,
- combinator: Combinator,
- selector: &SelectorIter<E::Impl>,
- context: &MatchingContext<E::Impl>,
-) -> Option<E>
-where
- E: Element,
-{
- match combinator {
- Combinator::NextSibling | Combinator::LaterSibling => element.prev_sibling_element(),
- Combinator::Child | Combinator::Descendant => {
- match element.parent_element() {
- Some(e) => return Some(e),
- None => {},
- }
-
- if !element.parent_node_is_shadow_root() {
- return None;
- }
-
- // https://drafts.csswg.org/css-scoping/#host-element-in-tree:
- //
- // For the purpose of Selectors, a shadow host also appears in
- // its shadow tree, with the contents of the shadow tree treated
- // as its children. (In other words, the shadow host is treated as
- // replacing the shadow root node.)
- //
- // and also:
- //
- // When considered within its own shadow trees, the shadow host is
- // featureless. Only the :host, :host(), and :host-context()
- // pseudo-classes are allowed to match it.
- //
- // Since we know that the parent is a shadow root, we necessarily
- // are in a shadow tree of the host, and the next selector will only
- // match if the selector is a featureless :host selector.
- if !selector.clone().is_featureless_host_selector() {
- return None;
- }
-
- element.containing_shadow_host()
- },
- Combinator::Part => element.containing_shadow_host(),
- Combinator::SlotAssignment => {
- debug_assert!(element
- .assigned_slot()
- .map_or(true, |s| s.is_html_slot_element()));
- let scope = context.current_host?;
- let mut current_slot = element.assigned_slot()?;
- while current_slot.containing_shadow_host().unwrap().opaque() != scope {
- current_slot = current_slot.assigned_slot()?;
- }
- Some(current_slot)
- },
- Combinator::PseudoElement => element.pseudo_element_originating_element(),
- }
-}
-
-fn matches_complex_selector_internal<E>(
- mut selector_iter: SelectorIter<E::Impl>,
- element: &E,
- context: &mut MatchingContext<E::Impl>,
- rightmost: Rightmost,
-) -> SelectorMatchingResult
-where
- E: Element,
-{
- debug!(
- "Matching complex selector {:?} for {:?}",
- selector_iter, element
- );
-
- let matches_compound_selector =
- matches_compound_selector(&mut selector_iter, element, context, rightmost);
-
- let combinator = selector_iter.next_sequence();
- if combinator.map_or(false, |c| c.is_sibling()) {
- if context.needs_selector_flags() {
- element.apply_selector_flags(ElementSelectorFlags::HAS_SLOW_SELECTOR_LATER_SIBLINGS);
- }
- }
-
- if !matches_compound_selector {
- return SelectorMatchingResult::NotMatchedAndRestartFromClosestLaterSibling;
- }
-
- let combinator = match combinator {
- None => return SelectorMatchingResult::Matched,
- Some(c) => c,
- };
-
- let candidate_not_found = match combinator {
- Combinator::NextSibling | Combinator::LaterSibling => {
- SelectorMatchingResult::NotMatchedAndRestartFromClosestDescendant
- },
- Combinator::Child |
- Combinator::Descendant |
- Combinator::SlotAssignment |
- Combinator::Part |
- Combinator::PseudoElement => SelectorMatchingResult::NotMatchedGlobally,
- };
-
- // Stop matching :visited as soon as we find a link, or a combinator for
- // something that isn't an ancestor.
- let mut visited_handling = if combinator.is_sibling() {
- VisitedHandlingMode::AllLinksUnvisited
- } else {
- context.visited_handling()
- };
-
- let mut element = element.clone();
- loop {
- if element.is_link() {
- visited_handling = VisitedHandlingMode::AllLinksUnvisited;
- }
-
- element = match next_element_for_combinator(&element, combinator, &selector_iter, &context)
- {
- None => return candidate_not_found,
- Some(next_element) => next_element,
- };
-
- let result = context.with_visited_handling_mode(visited_handling, |context| {
- matches_complex_selector_internal(
- selector_iter.clone(),
- &element,
- context,
- Rightmost::No,
- )
- });
-
- match (result, combinator) {
- // Return the status immediately.
- (SelectorMatchingResult::Matched, _) |
- (SelectorMatchingResult::NotMatchedGlobally, _) |
- (_, Combinator::NextSibling) => {
- return result;
- },
-
- // Upgrade the failure status to
- // NotMatchedAndRestartFromClosestDescendant.
- (_, Combinator::PseudoElement) | (_, Combinator::Child) => {
- return SelectorMatchingResult::NotMatchedAndRestartFromClosestDescendant;
- },
-
- // If the failure status is
- // NotMatchedAndRestartFromClosestDescendant and combinator is
- // Combinator::LaterSibling, give up this Combinator::LaterSibling
- // matching and restart from the closest descendant combinator.
- (
- SelectorMatchingResult::NotMatchedAndRestartFromClosestDescendant,
- Combinator::LaterSibling,
- ) => {
- return result;
- },
-
- // The Combinator::Descendant combinator and the status is
- // NotMatchedAndRestartFromClosestLaterSibling or
- // NotMatchedAndRestartFromClosestDescendant, or the
- // Combinator::LaterSibling combinator and the status is
- // NotMatchedAndRestartFromClosestDescendant, we can continue to
- // matching on the next candidate element.
- _ => {},
- }
- }
-}
-
-#[inline]
-fn matches_local_name<E>(element: &E, local_name: &LocalName<E::Impl>) -> bool
-where
- E: Element,
-{
- let name = select_name(element, &local_name.name, &local_name.lower_name).borrow();
- element.has_local_name(name)
-}
-
-/// Determines whether the given element matches the given compound selector.
-#[inline]
-fn matches_compound_selector<E>(
- selector_iter: &mut SelectorIter<E::Impl>,
- element: &E,
- context: &mut MatchingContext<E::Impl>,
- rightmost: Rightmost,
-) -> bool
-where
- E: Element,
-{
- let quirks_data = if context.quirks_mode() == QuirksMode::Quirks {
- Some((rightmost, selector_iter.clone()))
- } else {
- None
- };
-
- // Handle some common cases first.
- // We may want to get rid of this at some point if we can make the
- // generic case fast enough.
- let mut selector = selector_iter.next();
- if let Some(&Component::LocalName(ref local_name)) = selector {
- if !matches_local_name(element, local_name) {
- return false;
- }
- selector = selector_iter.next();
- }
- let class_and_id_case_sensitivity = context.classes_and_ids_case_sensitivity();
- if let Some(&Component::ID(ref id)) = selector {
- if !element.has_id(id, class_and_id_case_sensitivity) {
- return false;
- }
- selector = selector_iter.next();
- }
- while let Some(&Component::Class(ref class)) = selector {
- if !element.has_class(class, class_and_id_case_sensitivity) {
- return false;
- }
- selector = selector_iter.next();
- }
- let selector = match selector {
- Some(s) => s,
- None => return true,
- };
-
- let mut local_context = LocalMatchingContext {
- shared: context,
- quirks_data,
- };
- iter::once(selector)
- .chain(selector_iter)
- .all(|simple| matches_simple_selector(simple, element, &mut local_context))
-}
-
-/// Determines whether the given element matches the given single selector.
-fn matches_simple_selector<E>(
- selector: &Component<E::Impl>,
- element: &E,
- context: &mut LocalMatchingContext<E::Impl>,
-) -> bool
-where
- E: Element,
-{
- debug_assert!(context.shared.is_nested() || !context.shared.in_negation());
-
- match *selector {
- Component::ID(ref id) => {
- element.has_id(id, context.shared.classes_and_ids_case_sensitivity())
- },
- Component::Class(ref class) => {
- element.has_class(class, context.shared.classes_and_ids_case_sensitivity())
- },
- Component::LocalName(ref local_name) => matches_local_name(element, local_name),
- Component::AttributeInNoNamespaceExists {
- ref local_name,
- ref local_name_lower,
- } => element.has_attr_in_no_namespace(select_name(element, local_name, local_name_lower)),
- Component::AttributeInNoNamespace {
- ref local_name,
- ref value,
- operator,
- case_sensitivity,
- } => {
- element.attr_matches(
- &NamespaceConstraint::Specific(&crate::parser::namespace_empty_string::<E::Impl>()),
- local_name,
- &AttrSelectorOperation::WithValue {
- operator,
- case_sensitivity: to_unconditional_case_sensitivity(case_sensitivity, element),
- value,
- },
- )
- },
- Component::AttributeOther(ref attr_sel) => {
- let empty_string;
- let namespace = match attr_sel.namespace() {
- Some(ns) => ns,
- None => {
- empty_string = crate::parser::namespace_empty_string::<E::Impl>();
- NamespaceConstraint::Specific(&empty_string)
- },
- };
- element.attr_matches(
- &namespace,
- select_name(element, &attr_sel.local_name, &attr_sel.local_name_lower),
- &match attr_sel.operation {
- ParsedAttrSelectorOperation::Exists => AttrSelectorOperation::Exists,
- ParsedAttrSelectorOperation::WithValue {
- operator,
- case_sensitivity,
- ref value,
- } => AttrSelectorOperation::WithValue {
- operator,
- case_sensitivity: to_unconditional_case_sensitivity(
- case_sensitivity,
- element,
- ),
- value,
- },
- },
- )
- },
- Component::Part(ref parts) => {
- let mut hosts = SmallVec::<[E; 4]>::new();
-
- let mut host = match element.containing_shadow_host() {
- Some(h) => h,
- None => return false,
- };
-
- let current_host = context.shared.current_host;
- if current_host != Some(host.opaque()) {
- loop {
- let outer_host = host.containing_shadow_host();
- if outer_host.as_ref().map(|h| h.opaque()) == current_host {
- break;
- }
- let outer_host = match outer_host {
- Some(h) => h,
- None => return false,
- };
- // TODO(emilio): if worth it, we could early return if
- // host doesn't have the exportparts attribute.
- hosts.push(host);
- host = outer_host;
- }
- }
-
- // Translate the part into the right scope.
- parts.iter().all(|part| {
- let mut part = part.clone();
- for host in hosts.iter().rev() {
- part = match host.imported_part(&part) {
- Some(p) => p,
- None => return false,
- };
- }
- element.is_part(&part)
- })
- },
- Component::Slotted(ref selector) => {
- // <slots> are never flattened tree slottables.
- !element.is_html_slot_element() &&
- context
- .shared
- .nest(|context| matches_complex_selector(selector.iter(), element, context))
- },
- Component::PseudoElement(ref pseudo) => {
- element.match_pseudo_element(pseudo, context.shared)
- },
- Component::ExplicitUniversalType | Component::ExplicitAnyNamespace => true,
- Component::Namespace(_, ref url) | Component::DefaultNamespace(ref url) => {
- element.has_namespace(&url.borrow())
- },
- Component::ExplicitNoNamespace => {
- let ns = crate::parser::namespace_empty_string::<E::Impl>();
- element.has_namespace(&ns.borrow())
- },
- Component::NonTSPseudoClass(ref pc) => {
- if let Some((ref rightmost, ref iter)) = context.quirks_data {
- if pc.is_active_or_hover() &&
- !element.is_link() &&
- hover_and_active_quirk_applies(iter, context.shared, *rightmost)
- {
- return false;
- }
- }
- element.match_non_ts_pseudo_class(pc, &mut context.shared)
- },
- Component::Root => element.is_root(),
- Component::Empty => {
- if context.shared.needs_selector_flags() {
- element.apply_selector_flags(ElementSelectorFlags::HAS_EMPTY_SELECTOR);
- }
- element.is_empty()
- },
- Component::Host(ref selector) => {
- context
- .shared
- .shadow_host()
- .map_or(false, |host| host == element.opaque()) &&
- selector.as_ref().map_or(true, |selector| {
- context
- .shared
- .nest(|context| matches_complex_selector(selector.iter(), element, context))
- })
- },
- // These should only work at parse time, should be replaced with :is() at CascadeData build
- // time.
- Component::ParentSelector => false,
- Component::Scope => match context.shared.scope_element {
- Some(ref scope_element) => element.opaque() == *scope_element,
- None => element.is_root(),
- },
- Component::Nth(ref nth_data) => {
- matches_generic_nth_child(element, context.shared, nth_data, &[])
- },
- Component::NthOf(ref nth_of_data) => context.shared.nest(|context| {
- matches_generic_nth_child(
- element,
- context,
- nth_of_data.nth_data(),
- nth_of_data.selectors(),
- )
- }),
- Component::Is(ref list) | Component::Where(ref list) => context
- .shared
- .nest(|context| list_matches_complex_selector(list, element, context)),
- Component::Negation(ref list) => context
- .shared
- .nest_for_negation(|context| !list_matches_complex_selector(list, element, context)),
- Component::Has(ref relative_selectors) => context
- .shared
- .nest_for_relative_selector(element.opaque(), |context| {
- matches_relative_selectors(relative_selectors, element, context)
- }),
- Component::Combinator(_) => unsafe {
- debug_unreachable!("Shouldn't try to selector-match combinators")
- },
- Component::RelativeSelectorAnchor => {
- let anchor = context.shared.relative_selector_anchor();
- debug_assert!(
- anchor.is_some(),
- "Relative selector outside of relative selector matching?"
- );
- anchor.map_or(false, |a| a == element.opaque())
- },
- }
-}
-
-#[inline(always)]
-fn select_name<'a, E: Element, T: PartialEq>(
- element: &E,
- local_name: &'a T,
- local_name_lower: &'a T,
-) -> &'a T {
- if local_name == local_name_lower || element.is_html_element_in_html_document() {
- local_name_lower
- } else {
- local_name
- }
-}
-
-#[inline(always)]
-fn to_unconditional_case_sensitivity<'a, E: Element>(
- parsed: ParsedCaseSensitivity,
- element: &E,
-) -> CaseSensitivity {
- match parsed {
- ParsedCaseSensitivity::CaseSensitive | ParsedCaseSensitivity::ExplicitCaseSensitive => {
- CaseSensitivity::CaseSensitive
- },
- ParsedCaseSensitivity::AsciiCaseInsensitive => CaseSensitivity::AsciiCaseInsensitive,
- ParsedCaseSensitivity::AsciiCaseInsensitiveIfInHtmlElementInHtmlDocument => {
- if element.is_html_element_in_html_document() {
- CaseSensitivity::AsciiCaseInsensitive
- } else {
- CaseSensitivity::CaseSensitive
- }
- },
- }
-}
-
-fn matches_generic_nth_child<E>(
- element: &E,
- context: &mut MatchingContext<E::Impl>,
- nth_data: &NthSelectorData,
- selectors: &[Selector<E::Impl>],
-) -> bool
-where
- E: Element,
-{
- if element.ignores_nth_child_selectors() {
- return false;
- }
-
- let NthSelectorData { ty, a, b, .. } = *nth_data;
- let is_of_type = ty.is_of_type();
- if ty.is_only() {
- debug_assert!(
- selectors.is_empty(),
- ":only-child and :only-of-type cannot have a selector list!"
- );
- return matches_generic_nth_child(
- element,
- context,
- &NthSelectorData::first(is_of_type),
- selectors,
- ) && matches_generic_nth_child(
- element,
- context,
- &NthSelectorData::last(is_of_type),
- selectors,
- );
- }
-
- let is_from_end = ty.is_from_end();
-
- // It's useful to know whether this can only select the first/last element
- // child for optimization purposes, see the `HAS_EDGE_CHILD_SELECTOR` flag.
- let is_edge_child_selector = a == 0 && b == 1 && !is_of_type && selectors.is_empty();
-
- if context.needs_selector_flags() {
- let mut flags = if is_edge_child_selector {
- ElementSelectorFlags::HAS_EDGE_CHILD_SELECTOR
- } else if is_from_end {
- ElementSelectorFlags::HAS_SLOW_SELECTOR
- } else {
- ElementSelectorFlags::HAS_SLOW_SELECTOR_LATER_SIBLINGS
- };
- if !selectors.is_empty() {
- flags |= ElementSelectorFlags::HAS_SLOW_SELECTOR_NTH_OF;
- }
- element.apply_selector_flags(flags);
- }
-
- if !selectors.is_empty() && !list_matches_complex_selector(selectors, element, context) {
- return false;
- }
-
- // :first/last-child are rather trivial to match, don't bother with the
- // cache.
- if is_edge_child_selector {
- return if is_from_end {
- element.next_sibling_element()
- } else {
- element.prev_sibling_element()
- }
- .is_none();
- }
-
- // Lookup or compute the index.
- let index = if let Some(i) = context
- .nth_index_cache(is_of_type, is_from_end, selectors)
- .lookup(element.opaque())
- {
- i
- } else {
- let i = nth_child_index(
- element,
- context,
- selectors,
- is_of_type,
- is_from_end,
- /* check_cache = */ true,
- );
- context
- .nth_index_cache(is_of_type, is_from_end, selectors)
- .insert(element.opaque(), i);
- i
- };
- debug_assert_eq!(
- index,
- nth_child_index(
- element,
- context,
- selectors,
- is_of_type,
- is_from_end,
- /* check_cache = */ false
- ),
- "invalid cache"
- );
-
- // Is there a non-negative integer n such that An+B=index?
- match index.checked_sub(b) {
- None => false,
- Some(an) => match an.checked_div(a) {
- Some(n) => n >= 0 && a * n == an,
- None /* a == 0 */ => an == 0,
- },
- }
-}
-
-#[inline]
-fn nth_child_index<E>(
- element: &E,
- context: &mut MatchingContext<E::Impl>,
- selectors: &[Selector<E::Impl>],
- is_of_type: bool,
- is_from_end: bool,
- check_cache: bool,
-) -> i32
-where
- E: Element,
-{
- // The traversal mostly processes siblings left to right. So when we walk
- // siblings to the right when computing NthLast/NthLastOfType we're unlikely
- // to get cache hits along the way. As such, we take the hit of walking the
- // siblings to the left checking the cache in the is_from_end case (this
- // matches what Gecko does). The indices-from-the-left is handled during the
- // regular look further below.
- if check_cache &&
- is_from_end &&
- !context
- .nth_index_cache(is_of_type, is_from_end, selectors)
- .is_empty()
- {
- let mut index: i32 = 1;
- let mut curr = element.clone();
- while let Some(e) = curr.prev_sibling_element() {
- curr = e;
- let matches = if is_of_type {
- element.is_same_type(&curr)
- } else if !selectors.is_empty() {
- list_matches_complex_selector(selectors, &curr, context)
- } else {
- true
- };
- if !matches {
- continue;
- }
- if let Some(i) = context
- .nth_index_cache(is_of_type, is_from_end, selectors)
- .lookup(curr.opaque())
- {
- return i - index;
- }
- index += 1;
- }
- }
-
- let mut index: i32 = 1;
- let mut curr = element.clone();
- let next = |e: E| {
- if is_from_end {
- e.next_sibling_element()
- } else {
- e.prev_sibling_element()
- }
- };
- while let Some(e) = next(curr) {
- curr = e;
- let matches = if is_of_type {
- element.is_same_type(&curr)
- } else if !selectors.is_empty() {
- list_matches_complex_selector(selectors, &curr, context)
- } else {
- true
- };
- if !matches {
- continue;
- }
- // If we're computing indices from the left, check each element in the
- // cache. We handle the indices-from-the-right case at the top of this
- // function.
- if !is_from_end && check_cache {
- if let Some(i) = context
- .nth_index_cache(is_of_type, is_from_end, selectors)
- .lookup(curr.opaque())
- {
- return i + index;
- }
- }
- index += 1;
- }
-
- index
-}
diff --git a/components/selectors/nth_index_cache.rs b/components/selectors/nth_index_cache.rs
deleted file mode 100644
index b4b41578d02..00000000000
--- a/components/selectors/nth_index_cache.rs
+++ /dev/null
@@ -1,102 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
-
-use std::hash::Hash;
-
-use crate::{parser::Selector, tree::OpaqueElement, SelectorImpl};
-use fxhash::FxHashMap;
-
-/// A cache to speed up matching of nth-index-like selectors.
-///
-/// See [1] for some discussion around the design tradeoffs.
-///
-/// [1] https://bugzilla.mozilla.org/show_bug.cgi?id=1401855#c3
-#[derive(Default)]
-pub struct NthIndexCache {
- nth: NthIndexCacheInner,
- nth_of_selectors: NthIndexOfSelectorsCaches,
- nth_last: NthIndexCacheInner,
- nth_last_of_selectors: NthIndexOfSelectorsCaches,
- nth_of_type: NthIndexCacheInner,
- nth_last_of_type: NthIndexCacheInner,
-}
-
-impl NthIndexCache {
- /// Gets the appropriate cache for the given parameters.
- pub fn get<Impl: SelectorImpl>(
- &mut self,
- is_of_type: bool,
- is_from_end: bool,
- selectors: &[Selector<Impl>],
- ) -> &mut NthIndexCacheInner {
- if is_of_type {
- return if is_from_end {
- &mut self.nth_last_of_type
- } else {
- &mut self.nth_of_type
- };
- }
- if !selectors.is_empty() {
- return if is_from_end {
- self.nth_last_of_selectors.lookup(selectors)
- } else {
- self.nth_of_selectors.lookup(selectors)
- };
- }
- if is_from_end {
- &mut self.nth_last
- } else {
- &mut self.nth
- }
- }
-}
-
-#[derive(Hash, Eq, PartialEq)]
-struct SelectorListCacheKey(usize);
-
-/// Use the selector list's pointer as the cache key
-impl SelectorListCacheKey {
- // :nth-child of selectors are reference-counted with `ThinArc`, so we know their pointers are stable.
- fn new<Impl: SelectorImpl>(selectors: &[Selector<Impl>]) -> Self {
- Self(selectors.as_ptr() as usize)
- }
-}
-
-/// Use a different map of cached indices per :nth-child's or :nth-last-child's selector list
-#[derive(Default)]
-pub struct NthIndexOfSelectorsCaches(FxHashMap<SelectorListCacheKey, NthIndexCacheInner>);
-
-/// Get or insert a map of cached incides for the selector list of this
-/// particular :nth-child or :nth-last-child pseudoclass
-impl NthIndexOfSelectorsCaches {
- pub fn lookup<Impl: SelectorImpl>(
- &mut self,
- selectors: &[Selector<Impl>],
- ) -> &mut NthIndexCacheInner {
- self.0
- .entry(SelectorListCacheKey::new(selectors))
- .or_default()
- }
-}
-
-/// The concrete per-pseudo-class cache.
-#[derive(Default)]
-pub struct NthIndexCacheInner(FxHashMap<OpaqueElement, i32>);
-
-impl NthIndexCacheInner {
- /// Does a lookup for a given element in the cache.
- pub fn lookup(&mut self, el: OpaqueElement) -> Option<i32> {
- self.0.get(&el).copied()
- }
-
- /// Inserts an entry into the cache.
- pub fn insert(&mut self, element: OpaqueElement, index: i32) {
- self.0.insert(element, index);
- }
-
- /// Returns whether the cache is empty.
- pub fn is_empty(&self) -> bool {
- self.0.is_empty()
- }
-}
diff --git a/components/selectors/parser.rs b/components/selectors/parser.rs
deleted file mode 100644
index 2c44da8018e..00000000000
--- a/components/selectors/parser.rs
+++ /dev/null
@@ -1,4140 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
-
-use crate::attr::{AttrSelectorOperator, AttrSelectorWithOptionalNamespace};
-use crate::attr::{NamespaceConstraint, ParsedAttrSelectorOperation, ParsedCaseSensitivity};
-use crate::bloom::BLOOM_HASH_MASK;
-use crate::builder::{
- relative_selector_list_specificity_and_flags, selector_list_specificity_and_flags,
- SelectorBuilder, SelectorFlags, Specificity, SpecificityAndFlags,
-};
-use crate::context::QuirksMode;
-use crate::sink::Push;
-use crate::visitor::SelectorListKind;
-pub use crate::visitor::SelectorVisitor;
-use bitflags::bitflags;
-use cssparser::parse_nth;
-use cssparser::{BasicParseError, BasicParseErrorKind, ParseError, ParseErrorKind};
-use cssparser::{CowRcStr, Delimiter, SourceLocation};
-use cssparser::{Parser as CssParser, ToCss, Token};
-use precomputed_hash::PrecomputedHash;
-use servo_arc::{HeaderWithLength, ThinArc, UniqueArc};
-use smallvec::SmallVec;
-use std::borrow::{Borrow, Cow};
-use std::fmt::{self, Debug};
-use std::iter::Rev;
-use std::slice;
-
-/// A trait that represents a pseudo-element.
-pub trait PseudoElement: Sized + ToCss {
- /// The `SelectorImpl` this pseudo-element is used for.
- type Impl: SelectorImpl;
-
- /// Whether the pseudo-element supports a given state selector to the right
- /// of it.
- fn accepts_state_pseudo_classes(&self) -> bool {
- false
- }
-
- /// Whether this pseudo-element is valid after a ::slotted(..) pseudo.
- fn valid_after_slotted(&self) -> bool {
- false
- }
-}
-
-/// A trait that represents a pseudo-class.
-pub trait NonTSPseudoClass: Sized + ToCss {
- /// The `SelectorImpl` this pseudo-element is used for.
- type Impl: SelectorImpl;
-
- /// Whether this pseudo-class is :active or :hover.
- fn is_active_or_hover(&self) -> bool;
-
- /// Whether this pseudo-class belongs to:
- ///
- /// https://drafts.csswg.org/selectors-4/#useraction-pseudos
- fn is_user_action_state(&self) -> bool;
-
- fn visit<V>(&self, _visitor: &mut V) -> bool
- where
- V: SelectorVisitor<Impl = Self::Impl>,
- {
- true
- }
-}
-
-/// Returns a Cow::Borrowed if `s` is already ASCII lowercase, and a
-/// Cow::Owned if `s` had to be converted into ASCII lowercase.
-fn to_ascii_lowercase(s: &str) -> Cow<str> {
- if let Some(first_uppercase) = s.bytes().position(|byte| byte >= b'A' && byte <= b'Z') {
- let mut string = s.to_owned();
- string[first_uppercase..].make_ascii_lowercase();
- string.into()
- } else {
- s.into()
- }
-}
-
-bitflags! {
- /// Flags that indicate at which point of parsing a selector are we.
- struct SelectorParsingState: u8 {
- /// Whether we should avoid adding default namespaces to selectors that
- /// aren't type or universal selectors.
- const SKIP_DEFAULT_NAMESPACE = 1 << 0;
-
- /// Whether we've parsed a ::slotted() pseudo-element already.
- ///
- /// If so, then we can only parse a subset of pseudo-elements, and
- /// whatever comes after them if so.
- const AFTER_SLOTTED = 1 << 1;
- /// Whether we've parsed a ::part() pseudo-element already.
- ///
- /// If so, then we can only parse a subset of pseudo-elements, and
- /// whatever comes after them if so.
- const AFTER_PART = 1 << 2;
- /// Whether we've parsed a pseudo-element (as in, an
- /// `Impl::PseudoElement` thus not accounting for `::slotted` or
- /// `::part`) already.
- ///
- /// If so, then other pseudo-elements and most other selectors are
- /// disallowed.
- const AFTER_PSEUDO_ELEMENT = 1 << 3;
- /// Whether we've parsed a non-stateful pseudo-element (again, as-in
- /// `Impl::PseudoElement`) already. If so, then other pseudo-classes are
- /// disallowed. If this flag is set, `AFTER_PSEUDO_ELEMENT` must be set
- /// as well.
- const AFTER_NON_STATEFUL_PSEUDO_ELEMENT = 1 << 4;
-
- /// Whether we are after any of the pseudo-like things.
- const AFTER_PSEUDO = Self::AFTER_PART.bits | Self::AFTER_SLOTTED.bits | Self::AFTER_PSEUDO_ELEMENT.bits;
-
- /// Whether we explicitly disallow combinators.
- const DISALLOW_COMBINATORS = 1 << 5;
-
- /// Whether we explicitly disallow pseudo-element-like things.
- const DISALLOW_PSEUDOS = 1 << 6;
-
- /// Whether we explicitly disallow relative selectors (i.e. `:has()`).
- const DISALLOW_RELATIVE_SELECTOR = 1 << 7;
- }
-}
-
-impl SelectorParsingState {
- #[inline]
- fn allows_pseudos(self) -> bool {
- // NOTE(emilio): We allow pseudos after ::part and such.
- !self.intersects(Self::AFTER_PSEUDO_ELEMENT | Self::DISALLOW_PSEUDOS)
- }
-
- #[inline]
- fn allows_slotted(self) -> bool {
- !self.intersects(Self::AFTER_PSEUDO | Self::DISALLOW_PSEUDOS)
- }
-
- #[inline]
- fn allows_part(self) -> bool {
- !self.intersects(Self::AFTER_PSEUDO | Self::DISALLOW_PSEUDOS)
- }
-
- // TODO(emilio): Maybe some of these should be allowed, but this gets us on
- // the safe side for now, matching previous behavior. Gotta be careful with
- // the ones like :-moz-any, which allow nested selectors but don't carry the
- // state, and so on.
- #[inline]
- fn allows_custom_functional_pseudo_classes(self) -> bool {
- !self.intersects(Self::AFTER_PSEUDO)
- }
-
- #[inline]
- fn allows_non_functional_pseudo_classes(self) -> bool {
- !self.intersects(Self::AFTER_SLOTTED | Self::AFTER_NON_STATEFUL_PSEUDO_ELEMENT)
- }
-
- #[inline]
- fn allows_tree_structural_pseudo_classes(self) -> bool {
- !self.intersects(Self::AFTER_PSEUDO)
- }
-
- #[inline]
- fn allows_combinators(self) -> bool {
- !self.intersects(Self::DISALLOW_COMBINATORS)
- }
-}
-
-pub type SelectorParseError<'i> = ParseError<'i, SelectorParseErrorKind<'i>>;
-
-#[derive(Clone, Debug, PartialEq)]
-pub enum SelectorParseErrorKind<'i> {
- NoQualifiedNameInAttributeSelector(Token<'i>),
- EmptySelector,
- DanglingCombinator,
- NonCompoundSelector,
- NonPseudoElementAfterSlotted,
- InvalidPseudoElementAfterSlotted,
- InvalidPseudoElementInsideWhere,
- InvalidState,
- UnexpectedTokenInAttributeSelector(Token<'i>),
- PseudoElementExpectedColon(Token<'i>),
- PseudoElementExpectedIdent(Token<'i>),
- NoIdentForPseudo(Token<'i>),
- UnsupportedPseudoClassOrElement(CowRcStr<'i>),
- UnexpectedIdent(CowRcStr<'i>),
- ExpectedNamespace(CowRcStr<'i>),
- ExpectedBarInAttr(Token<'i>),
- BadValueInAttr(Token<'i>),
- InvalidQualNameInAttr(Token<'i>),
- ExplicitNamespaceUnexpectedToken(Token<'i>),
- ClassNeedsIdent(Token<'i>),
-}
-
-macro_rules! with_all_bounds {
- (
- [ $( $InSelector: tt )* ]
- [ $( $CommonBounds: tt )* ]
- [ $( $FromStr: tt )* ]
- ) => {
- /// This trait allows to define the parser implementation in regards
- /// of pseudo-classes/elements
- ///
- /// NB: We need Clone so that we can derive(Clone) on struct with that
- /// are parameterized on SelectorImpl. See
- /// <https://github.com/rust-lang/rust/issues/26925>
- pub trait SelectorImpl: Clone + Debug + Sized + 'static {
- type ExtraMatchingData<'a>: Sized + Default;
- type AttrValue: $($InSelector)*;
- type Identifier: $($InSelector)*;
- type LocalName: $($InSelector)* + Borrow<Self::BorrowedLocalName>;
- type NamespaceUrl: $($CommonBounds)* + Default + Borrow<Self::BorrowedNamespaceUrl>;
- type NamespacePrefix: $($InSelector)* + Default;
- type BorrowedNamespaceUrl: ?Sized + Eq;
- type BorrowedLocalName: ?Sized + Eq;
-
- /// non tree-structural pseudo-classes
- /// (see: https://drafts.csswg.org/selectors/#structural-pseudos)
- type NonTSPseudoClass: $($CommonBounds)* + NonTSPseudoClass<Impl = Self>;
-
- /// pseudo-elements
- type PseudoElement: $($CommonBounds)* + PseudoElement<Impl = Self>;
-
- /// Whether attribute hashes should be collected for filtering
- /// purposes.
- fn should_collect_attr_hash(_name: &Self::LocalName) -> bool {
- false
- }
- }
- }
-}
-
-macro_rules! with_bounds {
- ( [ $( $CommonBounds: tt )* ] [ $( $FromStr: tt )* ]) => {
- with_all_bounds! {
- [$($CommonBounds)* + $($FromStr)* + ToCss]
- [$($CommonBounds)*]
- [$($FromStr)*]
- }
- }
-}
-
-with_bounds! {
- [Clone + Eq]
- [for<'a> From<&'a str>]
-}
-
-pub trait Parser<'i> {
- type Impl: SelectorImpl;
- type Error: 'i + From<SelectorParseErrorKind<'i>>;
-
- /// Whether to parse the `::slotted()` pseudo-element.
- fn parse_slotted(&self) -> bool {
- false
- }
-
- /// Whether to parse the `::part()` pseudo-element.
- fn parse_part(&self) -> bool {
- false
- }
-
- /// Whether to parse the selector list of nth-child() or nth-last-child().
- fn parse_nth_child_of(&self) -> bool {
- false
- }
-
- /// Whether to parse the `:where` pseudo-class.
- fn parse_is_and_where(&self) -> bool {
- false
- }
-
- /// Whether to parse the :has pseudo-class.
- fn parse_has(&self) -> bool {
- false
- }
-
- /// Whether to parse the '&' delimiter as a parent selector.
- fn parse_parent_selector(&self) -> bool {
- false
- }
-
- /// Whether the given function name is an alias for the `:is()` function.
- fn is_is_alias(&self, _name: &str) -> bool {
- false
- }
-
- /// Whether to parse the `:host` pseudo-class.
- fn parse_host(&self) -> bool {
- false
- }
-
- /// Whether to allow forgiving selector-list parsing.
- fn allow_forgiving_selectors(&self) -> bool {
- true
- }
-
- /// This function can return an "Err" pseudo-element in order to support CSS2.1
- /// pseudo-elements.
- fn parse_non_ts_pseudo_class(
- &self,
- location: SourceLocation,
- name: CowRcStr<'i>,
- ) -> Result<<Self::Impl as SelectorImpl>::NonTSPseudoClass, ParseError<'i, Self::Error>> {
- Err(
- location.new_custom_error(SelectorParseErrorKind::UnsupportedPseudoClassOrElement(
- name,
- )),
- )
- }
-
- fn parse_non_ts_functional_pseudo_class<'t>(
- &self,
- name: CowRcStr<'i>,
- arguments: &mut CssParser<'i, 't>,
- ) -> Result<<Self::Impl as SelectorImpl>::NonTSPseudoClass, ParseError<'i, Self::Error>> {
- Err(
- arguments.new_custom_error(SelectorParseErrorKind::UnsupportedPseudoClassOrElement(
- name,
- )),
- )
- }
-
- fn parse_pseudo_element(
- &self,
- location: SourceLocation,
- name: CowRcStr<'i>,
- ) -> Result<<Self::Impl as SelectorImpl>::PseudoElement, ParseError<'i, Self::Error>> {
- Err(
- location.new_custom_error(SelectorParseErrorKind::UnsupportedPseudoClassOrElement(
- name,
- )),
- )
- }
-
- fn parse_functional_pseudo_element<'t>(
- &self,
- name: CowRcStr<'i>,
- arguments: &mut CssParser<'i, 't>,
- ) -> Result<<Self::Impl as SelectorImpl>::PseudoElement, ParseError<'i, Self::Error>> {
- Err(
- arguments.new_custom_error(SelectorParseErrorKind::UnsupportedPseudoClassOrElement(
- name,
- )),
- )
- }
-
- fn default_namespace(&self) -> Option<<Self::Impl as SelectorImpl>::NamespaceUrl> {
- None
- }
-
- fn namespace_for_prefix(
- &self,
- _prefix: &<Self::Impl as SelectorImpl>::NamespacePrefix,
- ) -> Option<<Self::Impl as SelectorImpl>::NamespaceUrl> {
- None
- }
-}
-
-#[derive(Clone, Debug, Eq, PartialEq, ToShmem)]
-#[shmem(no_bounds)]
-pub struct SelectorList<Impl: SelectorImpl>(
- #[shmem(field_bound)] pub SmallVec<[Selector<Impl>; 1]>,
-);
-
-/// Whether or not we're using forgiving parsing mode
-enum ForgivingParsing {
- /// Discard the entire selector list upon encountering any invalid selector.
- /// This is the default behavior for almost all of CSS.
- No,
- /// Ignore invalid selectors, potentially creating an empty selector list.
- ///
- /// This is the error recovery mode of :is() and :where()
- Yes,
-}
-
-/// Flag indicating if we're parsing relative selectors.
-#[derive(Copy, Clone, PartialEq)]
-enum ParseRelative {
- /// Expect selectors to start with a combinator, assuming descendant combinator if not present.
- Yes,
- /// Treat as parse error if any selector begins with a combinator.
- No,
-}
-
-impl<Impl: SelectorImpl> SelectorList<Impl> {
- /// Returns a selector list with a single `&`
- pub fn ampersand() -> Self {
- Self(smallvec::smallvec![Selector::ampersand()])
- }
-
- /// Parse a comma-separated list of Selectors.
- /// <https://drafts.csswg.org/selectors/#grouping>
- ///
- /// Return the Selectors or Err if there is an invalid selector.
- pub fn parse<'i, 't, P>(
- parser: &P,
- input: &mut CssParser<'i, 't>,
- ) -> Result<Self, ParseError<'i, P::Error>>
- where
- P: Parser<'i, Impl = Impl>,
- {
- Self::parse_with_state(
- parser,
- input,
- SelectorParsingState::empty(),
- ForgivingParsing::No,
- ParseRelative::No,
- )
- }
-
- #[inline]
- fn parse_with_state<'i, 't, P>(
- parser: &P,
- input: &mut CssParser<'i, 't>,
- state: SelectorParsingState,
- recovery: ForgivingParsing,
- parse_relative: ParseRelative,
- ) -> Result<Self, ParseError<'i, P::Error>>
- where
- P: Parser<'i, Impl = Impl>,
- {
- let mut values = SmallVec::new();
- loop {
- let selector = input.parse_until_before(Delimiter::Comma, |i| {
- parse_selector(parser, i, state, parse_relative)
- });
-
- let was_ok = selector.is_ok();
- match selector {
- Ok(selector) => values.push(selector),
- Err(err) => match recovery {
- ForgivingParsing::No => return Err(err),
- ForgivingParsing::Yes => {
- if !parser.allow_forgiving_selectors() {
- return Err(err);
- }
- },
- },
- }
-
- loop {
- match input.next() {
- Err(_) => return Ok(SelectorList(values)),
- Ok(&Token::Comma) => break,
- Ok(_) => {
- debug_assert!(!was_ok, "Shouldn't have got a selector if getting here");
- },
- }
- }
- }
- }
-
- /// Creates a SelectorList from a Vec of selectors. Used in tests.
- #[allow(dead_code)]
- pub(crate) fn from_vec(v: Vec<Selector<Impl>>) -> Self {
- SelectorList(SmallVec::from_vec(v))
- }
-}
-
-/// Parses one compound selector suitable for nested stuff like :-moz-any, etc.
-fn parse_inner_compound_selector<'i, 't, P, Impl>(
- parser: &P,
- input: &mut CssParser<'i, 't>,
- state: SelectorParsingState,
-) -> Result<Selector<Impl>, ParseError<'i, P::Error>>
-where
- P: Parser<'i, Impl = Impl>,
- Impl: SelectorImpl,
-{
- parse_selector(
- parser,
- input,
- state | SelectorParsingState::DISALLOW_PSEUDOS | SelectorParsingState::DISALLOW_COMBINATORS,
- ParseRelative::No,
- )
-}
-
-/// Ancestor hashes for the bloom filter. We precompute these and store them
-/// inline with selectors to optimize cache performance during matching.
-/// This matters a lot.
-///
-/// We use 4 hashes, which is copied from Gecko, who copied it from WebKit.
-/// Note that increasing the number of hashes here will adversely affect the
-/// cache hit when fast-rejecting long lists of Rules with inline hashes.
-///
-/// Because the bloom filter only uses the bottom 24 bits of the hash, we pack
-/// the fourth hash into the upper bits of the first three hashes in order to
-/// shrink Rule (whose size matters a lot). This scheme minimizes the runtime
-/// overhead of the packing for the first three hashes (we just need to mask
-/// off the upper bits) at the expense of making the fourth somewhat more
-/// complicated to assemble, because we often bail out before checking all the
-/// hashes.
-#[derive(Clone, Debug, Eq, PartialEq)]
-pub struct AncestorHashes {
- pub packed_hashes: [u32; 3],
-}
-
-fn collect_ancestor_hashes<Impl: SelectorImpl>(
- iter: SelectorIter<Impl>,
- quirks_mode: QuirksMode,
- hashes: &mut [u32; 4],
- len: &mut usize,
-) -> bool
-where
- Impl::Identifier: PrecomputedHash,
- Impl::LocalName: PrecomputedHash,
- Impl::NamespaceUrl: PrecomputedHash,
-{
- for component in AncestorIter::new(iter) {
- let hash = match *component {
- Component::LocalName(LocalName {
- ref name,
- ref lower_name,
- }) => {
- // Only insert the local-name into the filter if it's all
- // lowercase. Otherwise we would need to test both hashes, and
- // our data structures aren't really set up for that.
- if name != lower_name {
- continue;
- }
- name.precomputed_hash()
- },
- Component::DefaultNamespace(ref url) | Component::Namespace(_, ref url) => {
- url.precomputed_hash()
- },
- // In quirks mode, class and id selectors should match
- // case-insensitively, so just avoid inserting them into the filter.
- Component::ID(ref id) if quirks_mode != QuirksMode::Quirks => id.precomputed_hash(),
- Component::Class(ref class) if quirks_mode != QuirksMode::Quirks => {
- class.precomputed_hash()
- },
- Component::AttributeInNoNamespace { ref local_name, .. }
- if Impl::should_collect_attr_hash(local_name) =>
- {
- // AttributeInNoNamespace is only used when local_name ==
- // local_name_lower.
- local_name.precomputed_hash()
- },
- Component::AttributeInNoNamespaceExists {
- ref local_name,
- ref local_name_lower,
- ..
- } => {
- // Only insert the local-name into the filter if it's all
- // lowercase. Otherwise we would need to test both hashes, and
- // our data structures aren't really set up for that.
- if local_name != local_name_lower || !Impl::should_collect_attr_hash(local_name) {
- continue;
- }
- local_name.precomputed_hash()
- },
- Component::AttributeOther(ref selector) => {
- if selector.local_name != selector.local_name_lower ||
- !Impl::should_collect_attr_hash(&selector.local_name)
- {
- continue;
- }
- selector.local_name.precomputed_hash()
- },
- Component::Is(ref list) | Component::Where(ref list) => {
- // :where and :is OR their selectors, so we can't put any hash
- // in the filter if there's more than one selector, as that'd
- // exclude elements that may match one of the other selectors.
- if list.len() == 1 &&
- !collect_ancestor_hashes(list[0].iter(), quirks_mode, hashes, len)
- {
- return false;
- }
- continue;
- },
- _ => continue,
- };
-
- hashes[*len] = hash & BLOOM_HASH_MASK;
- *len += 1;
- if *len == hashes.len() {
- return false;
- }
- }
- true
-}
-
-impl AncestorHashes {
- pub fn new<Impl: SelectorImpl>(selector: &Selector<Impl>, quirks_mode: QuirksMode) -> Self
- where
- Impl::Identifier: PrecomputedHash,
- Impl::LocalName: PrecomputedHash,
- Impl::NamespaceUrl: PrecomputedHash,
- {
- // Compute ancestor hashes for the bloom filter.
- let mut hashes = [0u32; 4];
- let mut len = 0;
- collect_ancestor_hashes(selector.iter(), quirks_mode, &mut hashes, &mut len);
- debug_assert!(len <= 4);
-
- // Now, pack the fourth hash (if it exists) into the upper byte of each of
- // the other three hashes.
- if len == 4 {
- let fourth = hashes[3];
- hashes[0] |= (fourth & 0x000000ff) << 24;
- hashes[1] |= (fourth & 0x0000ff00) << 16;
- hashes[2] |= (fourth & 0x00ff0000) << 8;
- }
-
- AncestorHashes {
- packed_hashes: [hashes[0], hashes[1], hashes[2]],
- }
- }
-
- /// Returns the fourth hash, reassembled from parts.
- pub fn fourth_hash(&self) -> u32 {
- ((self.packed_hashes[0] & 0xff000000) >> 24) |
- ((self.packed_hashes[1] & 0xff000000) >> 16) |
- ((self.packed_hashes[2] & 0xff000000) >> 8)
- }
-}
-
-#[inline]
-pub fn namespace_empty_string<Impl: SelectorImpl>() -> Impl::NamespaceUrl {
- // Rust type’s default, not default namespace
- Impl::NamespaceUrl::default()
-}
-
-/// A Selector stores a sequence of simple selectors and combinators. The
-/// iterator classes allow callers to iterate at either the raw sequence level or
-/// at the level of sequences of simple selectors separated by combinators. Most
-/// callers want the higher-level iterator.
-///
-/// We store compound selectors internally right-to-left (in matching order).
-/// Additionally, we invert the order of top-level compound selectors so that
-/// each one matches left-to-right. This is because matching namespace, local name,
-/// id, and class are all relatively cheap, whereas matching pseudo-classes might
-/// be expensive (depending on the pseudo-class). Since authors tend to put the
-/// pseudo-classes on the right, it's faster to start matching on the left.
-///
-/// This reordering doesn't change the semantics of selector matching, and we
-/// handle it in to_css to make it invisible to serialization.
-#[derive(Clone, Eq, PartialEq, ToShmem)]
-#[shmem(no_bounds)]
-pub struct Selector<Impl: SelectorImpl>(
- #[shmem(field_bound)] ThinArc<SpecificityAndFlags, Component<Impl>>,
-);
-
-impl<Impl: SelectorImpl> Selector<Impl> {
- /// See Arc::mark_as_intentionally_leaked
- pub fn mark_as_intentionally_leaked(&self) {
- self.0.with_arc(|a| a.mark_as_intentionally_leaked())
- }
-
- fn ampersand() -> Self {
- Self(ThinArc::from_header_and_iter(
- SpecificityAndFlags {
- specificity: 0,
- flags: SelectorFlags::HAS_PARENT,
- },
- std::iter::once(Component::ParentSelector),
- ))
- }
-
- #[inline]
- pub fn specificity(&self) -> u32 {
- self.0.header.header.specificity()
- }
-
- #[inline]
- fn flags(&self) -> SelectorFlags {
- self.0.header.header.flags
- }
-
- #[inline]
- pub fn has_pseudo_element(&self) -> bool {
- self.0.header.header.has_pseudo_element()
- }
-
- #[inline]
- pub fn has_parent_selector(&self) -> bool {
- self.0.header.header.has_parent_selector()
- }
-
- #[inline]
- pub fn is_slotted(&self) -> bool {
- self.0.header.header.is_slotted()
- }
-
- #[inline]
- pub fn is_part(&self) -> bool {
- self.0.header.header.is_part()
- }
-
- #[inline]
- pub fn parts(&self) -> Option<&[Impl::Identifier]> {
- if !self.is_part() {
- return None;
- }
-
- let mut iter = self.iter();
- if self.has_pseudo_element() {
- // Skip the pseudo-element.
- for _ in &mut iter {}
-
- let combinator = iter.next_sequence()?;
- debug_assert_eq!(combinator, Combinator::PseudoElement);
- }
-
- for component in iter {
- if let Component::Part(ref part) = *component {
- return Some(part);
- }
- }
-
- debug_assert!(false, "is_part() lied somehow?");
- None
- }
-
- #[inline]
- pub fn pseudo_element(&self) -> Option<&Impl::PseudoElement> {
- if !self.has_pseudo_element() {
- return None;
- }
-
- for component in self.iter() {
- if let Component::PseudoElement(ref pseudo) = *component {
- return Some(pseudo);
- }
- }
-
- debug_assert!(false, "has_pseudo_element lied!");
- None
- }
-
- /// Whether this selector (pseudo-element part excluded) matches every element.
- ///
- /// Used for "pre-computed" pseudo-elements in components/style/stylist.rs
- #[inline]
- pub fn is_universal(&self) -> bool {
- self.iter_raw_match_order().all(|c| {
- matches!(
- *c,
- Component::ExplicitUniversalType |
- Component::ExplicitAnyNamespace |
- Component::Combinator(Combinator::PseudoElement) |
- Component::PseudoElement(..)
- )
- })
- }
-
- /// Returns an iterator over this selector in matching order (right-to-left).
- /// When a combinator is reached, the iterator will return None, and
- /// next_sequence() may be called to continue to the next sequence.
- #[inline]
- pub fn iter(&self) -> SelectorIter<Impl> {
- SelectorIter {
- iter: self.iter_raw_match_order(),
- next_combinator: None,
- }
- }
-
- /// Same as `iter()`, but skips `RelativeSelectorAnchor` and its associated combinator.
- #[inline]
- pub fn iter_skip_relative_selector_anchor(&self) -> SelectorIter<Impl> {
- if cfg!(debug_assertions) {
- let mut selector_iter = self.iter_raw_parse_order_from(0);
- assert!(
- matches!(
- selector_iter.next().unwrap(),
- Component::RelativeSelectorAnchor
- ),
- "Relative selector does not start with RelativeSelectorAnchor"
- );
- assert!(
- selector_iter.next().unwrap().is_combinator(),
- "Relative combinator does not exist"
- );
- }
-
- SelectorIter {
- iter: self.0.slice[..self.len() - 2].iter(),
- next_combinator: None,
- }
- }
-
- /// Whether this selector is a featureless :host selector, with no
- /// combinators to the left, and optionally has a pseudo-element to the
- /// right.
- #[inline]
- pub fn is_featureless_host_selector_or_pseudo_element(&self) -> bool {
- let mut iter = self.iter();
- if !self.has_pseudo_element() {
- return iter.is_featureless_host_selector();
- }
-
- // Skip the pseudo-element.
- for _ in &mut iter {}
-
- match iter.next_sequence() {
- None => return false,
- Some(combinator) => {
- debug_assert_eq!(combinator, Combinator::PseudoElement);
- },
- }
-
- iter.is_featureless_host_selector()
- }
-
- /// Returns an iterator over this selector in matching order (right-to-left),
- /// skipping the rightmost |offset| Components.
- #[inline]
- pub fn iter_from(&self, offset: usize) -> SelectorIter<Impl> {
- let iter = self.0.slice[offset..].iter();
- SelectorIter {
- iter,
- next_combinator: None,
- }
- }
-
- /// Returns the combinator at index `index` (zero-indexed from the right),
- /// or panics if the component is not a combinator.
- #[inline]
- pub fn combinator_at_match_order(&self, index: usize) -> Combinator {
- match self.0.slice[index] {
- Component::Combinator(c) => c,
- ref other => panic!(
- "Not a combinator: {:?}, {:?}, index: {}",
- other, self, index
- ),
- }
- }
-
- /// Returns an iterator over the entire sequence of simple selectors and
- /// combinators, in matching order (from right to left).
- #[inline]
- pub fn iter_raw_match_order(&self) -> slice::Iter<Component<Impl>> {
- self.0.slice.iter()
- }
-
- /// Returns the combinator at index `index` (zero-indexed from the left),
- /// or panics if the component is not a combinator.
- #[inline]
- pub fn combinator_at_parse_order(&self, index: usize) -> Combinator {
- match self.0.slice[self.len() - index - 1] {
- Component::Combinator(c) => c,
- ref other => panic!(
- "Not a combinator: {:?}, {:?}, index: {}",
- other, self, index
- ),
- }
- }
-
- /// Returns an iterator over the sequence of simple selectors and
- /// combinators, in parse order (from left to right), starting from
- /// `offset`.
- #[inline]
- pub fn iter_raw_parse_order_from(&self, offset: usize) -> Rev<slice::Iter<Component<Impl>>> {
- self.0.slice[..self.len() - offset].iter().rev()
- }
-
- /// Creates a Selector from a vec of Components, specified in parse order. Used in tests.
- #[allow(dead_code)]
- pub(crate) fn from_vec(
- vec: Vec<Component<Impl>>,
- specificity: u32,
- flags: SelectorFlags,
- ) -> Self {
- let mut builder = SelectorBuilder::default();
- for component in vec.into_iter() {
- if let Some(combinator) = component.as_combinator() {
- builder.push_combinator(combinator);
- } else {
- builder.push_simple_selector(component);
- }
- }
- let spec = SpecificityAndFlags { specificity, flags };
- Selector(builder.build_with_specificity_and_flags(spec))
- }
-
- pub fn replace_parent_selector(&self, parent: &[Selector<Impl>]) -> Self {
- // FIXME(emilio): Shouldn't allow replacing if parent has a pseudo-element selector
- // or what not.
- let flags = self.flags() - SelectorFlags::HAS_PARENT;
- let mut specificity = Specificity::from(self.specificity());
- let parent_specificity =
- Specificity::from(selector_list_specificity_and_flags(parent.iter()).specificity());
-
- // The specificity at this point will be wrong, we replace it by the correct one after the
- // fact.
- let specificity_and_flags = SpecificityAndFlags {
- specificity: self.specificity(),
- flags,
- };
-
- fn replace_parent_on_selector_list<Impl: SelectorImpl>(
- orig: &[Selector<Impl>],
- parent: &[Selector<Impl>],
- specificity: &mut Specificity,
- with_specificity: bool,
- ) -> Vec<Selector<Impl>> {
- let mut any = false;
-
- let result = orig
- .iter()
- .map(|s| {
- if !s.has_parent_selector() {
- return s.clone();
- }
- any = true;
- s.replace_parent_selector(parent)
- })
- .collect();
-
- if !any || !with_specificity {
- return result;
- }
-
- *specificity += Specificity::from(
- selector_list_specificity_and_flags(result.iter()).specificity -
- selector_list_specificity_and_flags(orig.iter()).specificity,
- );
- result
- }
-
- fn replace_parent_on_relative_selector_list<Impl: SelectorImpl>(
- orig: &[RelativeSelector<Impl>],
- parent: &[Selector<Impl>],
- specificity: &mut Specificity,
- ) -> Vec<RelativeSelector<Impl>> {
- let mut any = false;
-
- let result = orig
- .iter()
- .map(|s| {
- if !s.selector.has_parent_selector() {
- return s.clone();
- }
- any = true;
- RelativeSelector {
- match_hint: s.match_hint,
- selector: s.selector.replace_parent_selector(parent),
- }
- })
- .collect();
-
- if !any {
- return result;
- }
-
- *specificity += Specificity::from(
- relative_selector_list_specificity_and_flags(&result).specificity -
- relative_selector_list_specificity_and_flags(orig).specificity,
- );
- result
- }
-
- fn replace_parent_on_selector<Impl: SelectorImpl>(
- orig: &Selector<Impl>,
- parent: &[Selector<Impl>],
- specificity: &mut Specificity,
- ) -> Selector<Impl> {
- if !orig.has_parent_selector() {
- return orig.clone();
- }
- let new_selector = orig.replace_parent_selector(parent);
- *specificity += Specificity::from(new_selector.specificity() - orig.specificity());
- new_selector
- }
-
- let mut items = if !self.has_parent_selector() {
- // Implicit `&` plus descendant combinator.
- let iter = self.iter_raw_match_order();
- let len = iter.len() + 2;
- specificity += parent_specificity;
- let iter = iter
- .cloned()
- .chain(std::iter::once(Component::Combinator(
- Combinator::Descendant,
- )))
- .chain(std::iter::once(Component::Is(
- parent.to_vec().into_boxed_slice(),
- )));
- let header = HeaderWithLength::new(specificity_and_flags, len);
- UniqueArc::from_header_and_iter_with_size(header, iter, len)
- } else {
- let iter = self.iter_raw_match_order().map(|component| {
- use self::Component::*;
- match *component {
- LocalName(..) |
- ID(..) |
- Class(..) |
- AttributeInNoNamespaceExists { .. } |
- AttributeInNoNamespace { .. } |
- AttributeOther(..) |
- ExplicitUniversalType |
- ExplicitAnyNamespace |
- ExplicitNoNamespace |
- DefaultNamespace(..) |
- Namespace(..) |
- Root |
- Empty |
- Scope |
- Nth(..) |
- NonTSPseudoClass(..) |
- PseudoElement(..) |
- Combinator(..) |
- Host(None) |
- Part(..) |
- RelativeSelectorAnchor => component.clone(),
- ParentSelector => {
- specificity += parent_specificity;
- Is(parent.to_vec().into_boxed_slice())
- },
- Negation(ref selectors) => {
- Negation(
- replace_parent_on_selector_list(
- selectors,
- parent,
- &mut specificity,
- /* with_specificity = */ true,
- )
- .into_boxed_slice(),
- )
- },
- Is(ref selectors) => {
- Is(replace_parent_on_selector_list(
- selectors,
- parent,
- &mut specificity,
- /* with_specificity = */ true,
- )
- .into_boxed_slice())
- },
- Where(ref selectors) => {
- Where(
- replace_parent_on_selector_list(
- selectors,
- parent,
- &mut specificity,
- /* with_specificity = */ false,
- )
- .into_boxed_slice(),
- )
- },
- Has(ref selectors) => Has(replace_parent_on_relative_selector_list(
- selectors,
- parent,
- &mut specificity,
- )
- .into_boxed_slice()),
-
- Host(Some(ref selector)) => Host(Some(replace_parent_on_selector(
- selector,
- parent,
- &mut specificity,
- ))),
- NthOf(ref data) => {
- let selectors = replace_parent_on_selector_list(
- data.selectors(),
- parent,
- &mut specificity,
- /* with_specificity = */ true,
- );
- NthOf(NthOfSelectorData::new(
- data.nth_data(),
- selectors.into_iter(),
- ))
- },
- Slotted(ref selector) => Slotted(replace_parent_on_selector(
- selector,
- parent,
- &mut specificity,
- )),
- }
- });
- let header = HeaderWithLength::new(specificity_and_flags, iter.len());
- UniqueArc::from_header_and_iter(header, iter)
- };
- items.header_mut().specificity = specificity.into();
- Selector(items.shareable_thin())
- }
-
- /// Returns count of simple selectors and combinators in the Selector.
- #[inline]
- pub fn len(&self) -> usize {
- self.0.slice.len()
- }
-
- /// Returns the address on the heap of the ThinArc for memory reporting.
- pub fn thin_arc_heap_ptr(&self) -> *const ::std::os::raw::c_void {
- self.0.heap_ptr()
- }
-
- /// Traverse selector components inside `self`.
- ///
- /// Implementations of this method should call `SelectorVisitor` methods
- /// or other impls of `Visit` as appropriate based on the fields of `Self`.
- ///
- /// A return value of `false` indicates terminating the traversal.
- /// It should be propagated with an early return.
- /// On the contrary, `true` indicates that all fields of `self` have been traversed:
- ///
- /// ```rust,ignore
- /// if !visitor.visit_simple_selector(&self.some_simple_selector) {
- /// return false;
- /// }
- /// if !self.some_component.visit(visitor) {
- /// return false;
- /// }
- /// true
- /// ```
- pub fn visit<V>(&self, visitor: &mut V) -> bool
- where
- V: SelectorVisitor<Impl = Impl>,
- {
- let mut current = self.iter();
- let mut combinator = None;
- loop {
- if !visitor.visit_complex_selector(combinator) {
- return false;
- }
-
- for selector in &mut current {
- if !selector.visit(visitor) {
- return false;
- }
- }
-
- combinator = current.next_sequence();
- if combinator.is_none() {
- break;
- }
- }
-
- true
- }
-}
-
-#[derive(Clone)]
-pub struct SelectorIter<'a, Impl: 'a + SelectorImpl> {
- iter: slice::Iter<'a, Component<Impl>>,
- next_combinator: Option<Combinator>,
-}
-
-impl<'a, Impl: 'a + SelectorImpl> SelectorIter<'a, Impl> {
- /// Prepares this iterator to point to the next sequence to the left,
- /// returning the combinator if the sequence was found.
- #[inline]
- pub fn next_sequence(&mut self) -> Option<Combinator> {
- self.next_combinator.take()
- }
-
- /// Whether this selector is a featureless host selector, with no
- /// combinators to the left.
- #[inline]
- pub(crate) fn is_featureless_host_selector(&mut self) -> bool {
- self.selector_length() > 0 &&
- self.all(|component| component.is_host()) &&
- self.next_sequence().is_none()
- }
-
- #[inline]
- pub(crate) fn matches_for_stateless_pseudo_element(&mut self) -> bool {
- let first = match self.next() {
- Some(c) => c,
- // Note that this is the common path that we keep inline: the
- // pseudo-element not having anything to its right.
- None => return true,
- };
- self.matches_for_stateless_pseudo_element_internal(first)
- }
-
- #[inline(never)]
- fn matches_for_stateless_pseudo_element_internal(&mut self, first: &Component<Impl>) -> bool {
- if !first.matches_for_stateless_pseudo_element() {
- return false;
- }
- for component in self {
- // The only other parser-allowed Components in this sequence are
- // state pseudo-classes, or one of the other things that can contain
- // them.
- if !component.matches_for_stateless_pseudo_element() {
- return false;
- }
- }
- true
- }
-
- /// Returns remaining count of the simple selectors and combinators in the Selector.
- #[inline]
- pub fn selector_length(&self) -> usize {
- self.iter.len()
- }
-}
-
-impl<'a, Impl: SelectorImpl> Iterator for SelectorIter<'a, Impl> {
- type Item = &'a Component<Impl>;
-
- #[inline]
- fn next(&mut self) -> Option<Self::Item> {
- debug_assert!(
- self.next_combinator.is_none(),
- "You should call next_sequence!"
- );
- match *self.iter.next()? {
- Component::Combinator(c) => {
- self.next_combinator = Some(c);
- None
- },
- ref x => Some(x),
- }
- }
-}
-
-impl<'a, Impl: SelectorImpl> fmt::Debug for SelectorIter<'a, Impl> {
- fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
- let iter = self.iter.clone().rev();
- for component in iter {
- component.to_css(f)?
- }
- Ok(())
- }
-}
-
-/// An iterator over all combinators in a selector. Does not traverse selectors within psuedoclasses.
-struct CombinatorIter<'a, Impl: 'a + SelectorImpl>(SelectorIter<'a, Impl>);
-impl<'a, Impl: 'a + SelectorImpl> CombinatorIter<'a, Impl> {
- fn new(inner: SelectorIter<'a, Impl>) -> Self {
- let mut result = CombinatorIter(inner);
- result.consume_non_combinators();
- result
- }
-
- fn consume_non_combinators(&mut self) {
- while self.0.next().is_some() {}
- }
-}
-
-impl<'a, Impl: SelectorImpl> Iterator for CombinatorIter<'a, Impl> {
- type Item = Combinator;
- fn next(&mut self) -> Option<Self::Item> {
- let result = self.0.next_sequence();
- self.consume_non_combinators();
- result
- }
-}
-
-/// An iterator over all simple selectors belonging to ancestors.
-struct AncestorIter<'a, Impl: 'a + SelectorImpl>(SelectorIter<'a, Impl>);
-impl<'a, Impl: 'a + SelectorImpl> AncestorIter<'a, Impl> {
- /// Creates an AncestorIter. The passed-in iterator is assumed to point to
- /// the beginning of the child sequence, which will be skipped.
- fn new(inner: SelectorIter<'a, Impl>) -> Self {
- let mut result = AncestorIter(inner);
- result.skip_until_ancestor();
- result
- }
-
- /// Skips a sequence of simple selectors and all subsequent sequences until
- /// a non-pseudo-element ancestor combinator is reached.
- fn skip_until_ancestor(&mut self) {
- loop {
- while self.0.next().is_some() {}
- // If this is ever changed to stop at the "pseudo-element"
- // combinator, we will need to fix the way we compute hashes for
- // revalidation selectors.
- if self.0.next_sequence().map_or(true, |x| {
- matches!(x, Combinator::Child | Combinator::Descendant)
- }) {
- break;
- }
- }
- }
-}
-
-impl<'a, Impl: SelectorImpl> Iterator for AncestorIter<'a, Impl> {
- type Item = &'a Component<Impl>;
- fn next(&mut self) -> Option<Self::Item> {
- // Grab the next simple selector in the sequence if available.
- let next = self.0.next();
- if next.is_some() {
- return next;
- }
-
- // See if there are more sequences. If so, skip any non-ancestor sequences.
- if let Some(combinator) = self.0.next_sequence() {
- if !matches!(combinator, Combinator::Child | Combinator::Descendant) {
- self.skip_until_ancestor();
- }
- }
-
- self.0.next()
- }
-}
-
-#[derive(Clone, Copy, Debug, Eq, PartialEq, ToShmem)]
-pub enum Combinator {
- Child, // >
- Descendant, // space
- NextSibling, // +
- LaterSibling, // ~
- /// A dummy combinator we use to the left of pseudo-elements.
- ///
- /// It serializes as the empty string, and acts effectively as a child
- /// combinator in most cases. If we ever actually start using a child
- /// combinator for this, we will need to fix up the way hashes are computed
- /// for revalidation selectors.
- PseudoElement,
- /// Another combinator used for ::slotted(), which represent the jump from
- /// a node to its assigned slot.
- SlotAssignment,
- /// Another combinator used for `::part()`, which represents the jump from
- /// the part to the containing shadow host.
- Part,
-}
-
-impl Combinator {
- /// Returns true if this combinator is a child or descendant combinator.
- #[inline]
- pub fn is_ancestor(&self) -> bool {
- matches!(
- *self,
- Combinator::Child |
- Combinator::Descendant |
- Combinator::PseudoElement |
- Combinator::SlotAssignment
- )
- }
-
- /// Returns true if this combinator is a pseudo-element combinator.
- #[inline]
- pub fn is_pseudo_element(&self) -> bool {
- matches!(*self, Combinator::PseudoElement)
- }
-
- /// Returns true if this combinator is a next- or later-sibling combinator.
- #[inline]
- pub fn is_sibling(&self) -> bool {
- matches!(*self, Combinator::NextSibling | Combinator::LaterSibling)
- }
-}
-
-/// An enum for the different types of :nth- pseudoclasses
-#[derive(Copy, Clone, Eq, PartialEq, ToShmem)]
-#[shmem(no_bounds)]
-pub enum NthType {
- Child,
- LastChild,
- OnlyChild,
- OfType,
- LastOfType,
- OnlyOfType,
-}
-
-impl NthType {
- pub fn is_only(self) -> bool {
- self == Self::OnlyChild || self == Self::OnlyOfType
- }
-
- pub fn is_of_type(self) -> bool {
- self == Self::OfType || self == Self::LastOfType || self == Self::OnlyOfType
- }
-
- pub fn is_from_end(self) -> bool {
- self == Self::LastChild || self == Self::LastOfType
- }
-}
-
-/// The properties that comprise an :nth- pseudoclass as of Selectors 3 (e.g.,
-/// nth-child(An+B)).
-/// https://www.w3.org/TR/selectors-3/#nth-child-pseudo
-#[derive(Copy, Clone, Eq, PartialEq, ToShmem)]
-#[shmem(no_bounds)]
-pub struct NthSelectorData {
- pub ty: NthType,
- pub is_function: bool,
- pub a: i32,
- pub b: i32,
-}
-
-impl NthSelectorData {
- /// Returns selector data for :only-{child,of-type}
- #[inline]
- pub const fn only(of_type: bool) -> Self {
- Self {
- ty: if of_type {
- NthType::OnlyOfType
- } else {
- NthType::OnlyChild
- },
- is_function: false,
- a: 0,
- b: 1,
- }
- }
-
- /// Returns selector data for :first-{child,of-type}
- #[inline]
- pub const fn first(of_type: bool) -> Self {
- Self {
- ty: if of_type {
- NthType::OfType
- } else {
- NthType::Child
- },
- is_function: false,
- a: 0,
- b: 1,
- }
- }
-
- /// Returns selector data for :last-{child,of-type}
- #[inline]
- pub const fn last(of_type: bool) -> Self {
- Self {
- ty: if of_type {
- NthType::LastOfType
- } else {
- NthType::LastChild
- },
- is_function: false,
- a: 0,
- b: 1,
- }
- }
-
- /// Writes the beginning of the selector.
- #[inline]
- fn write_start<W: fmt::Write>(&self, dest: &mut W) -> fmt::Result {
- dest.write_str(match self.ty {
- NthType::Child if self.is_function => ":nth-child(",
- NthType::Child => ":first-child",
- NthType::LastChild if self.is_function => ":nth-last-child(",
- NthType::LastChild => ":last-child",
- NthType::OfType if self.is_function => ":nth-of-type(",
- NthType::OfType => ":first-of-type",
- NthType::LastOfType if self.is_function => ":nth-last-of-type(",
- NthType::LastOfType => ":last-of-type",
- NthType::OnlyChild => ":only-child",
- NthType::OnlyOfType => ":only-of-type",
- })
- }
-
- /// Serialize <an+b> (part of the CSS Syntax spec, but currently only used here).
- /// <https://drafts.csswg.org/css-syntax-3/#serialize-an-anb-value>
- #[inline]
- fn write_affine<W: fmt::Write>(&self, dest: &mut W) -> fmt::Result {
- match (self.a, self.b) {
- (0, 0) => dest.write_char('0'),
-
- (1, 0) => dest.write_char('n'),
- (-1, 0) => dest.write_str("-n"),
- (_, 0) => write!(dest, "{}n", self.a),
-
- (0, _) => write!(dest, "{}", self.b),
- (1, _) => write!(dest, "n{:+}", self.b),
- (-1, _) => write!(dest, "-n{:+}", self.b),
- (_, _) => write!(dest, "{}n{:+}", self.a, self.b),
- }
- }
-}
-
-/// The properties that comprise an :nth- pseudoclass as of Selectors 4 (e.g.,
-/// nth-child(An+B [of S]?)).
-/// https://www.w3.org/TR/selectors-4/#nth-child-pseudo
-#[derive(Clone, Eq, PartialEq, ToShmem)]
-#[shmem(no_bounds)]
-pub struct NthOfSelectorData<Impl: SelectorImpl>(
- #[shmem(field_bound)] ThinArc<NthSelectorData, Selector<Impl>>,
-);
-
-impl<Impl: SelectorImpl> NthOfSelectorData<Impl> {
- /// Returns selector data for :nth-{,last-}{child,of-type}(An+B [of S])
- #[inline]
- pub fn new<I>(nth_data: &NthSelectorData, selectors: I) -> Self
- where
- I: Iterator<Item = Selector<Impl>> + ExactSizeIterator,
- {
- Self(ThinArc::from_header_and_iter(*nth_data, selectors))
- }
-
- /// Returns the An+B part of the selector
- #[inline]
- pub fn nth_data(&self) -> &NthSelectorData {
- &self.0.header.header
- }
-
- /// Returns the selector list part of the selector
- #[inline]
- pub fn selectors(&self) -> &[Selector<Impl>] {
- &self.0.slice
- }
-}
-
-/// Flag indicating where a given relative selector's match would be contained.
-#[derive(Clone, Copy, Eq, PartialEq, ToShmem)]
-pub enum RelativeSelectorMatchHint {
- /// Within this element's subtree.
- InSubtree,
- /// Within this element's direct children.
- InChild,
- /// This element's next sibling.
- InNextSibling,
- /// Within this element's next sibling's subtree.
- InNextSiblingSubtree,
- /// Within this element's subsequent siblings.
- InSibling,
- /// Across this element's subsequent siblings and their subtrees.
- InSiblingSubtree,
-}
-
-/// Storage for a relative selector.
-#[derive(Clone, Eq, PartialEq, ToShmem)]
-#[shmem(no_bounds)]
-pub struct RelativeSelector<Impl: SelectorImpl> {
- /// Match space constraining hint.
- pub match_hint: RelativeSelectorMatchHint,
- /// The selector. Guaranteed to contain `RelativeSelectorAnchor` and the relative combinator in parse order.
- #[shmem(field_bound)]
- pub selector: Selector<Impl>,
-}
-
-bitflags! {
- /// Composition of combinators in a given selector, not traversing selectors of pseudoclasses.
- struct CombinatorComposition: u8 {
- const DESCENDANTS = 1 << 0;
- const SIBLINGS = 1 << 1;
- }
-}
-
-impl CombinatorComposition {
- fn for_relative_selector<Impl: SelectorImpl>(inner_selector: &Selector<Impl>) -> Self {
- let mut result = CombinatorComposition::empty();
- for combinator in CombinatorIter::new(inner_selector.iter_skip_relative_selector_anchor()) {
- match combinator {
- Combinator::Descendant | Combinator::Child => {
- result.insert(Self::DESCENDANTS);
- },
- Combinator::NextSibling | Combinator::LaterSibling => {
- result.insert(Self::SIBLINGS);
- },
- Combinator::Part | Combinator::PseudoElement | Combinator::SlotAssignment => {
- continue
- },
- };
- if result.is_all() {
- break;
- }
- }
- return result;
- }
-}
-
-impl<Impl: SelectorImpl> RelativeSelector<Impl> {
- fn from_selector_list(selector_list: SelectorList<Impl>) -> Box<[Self]> {
- let vec: Vec<Self> = selector_list
- .0
- .into_iter()
- .map(|selector| {
- // It's more efficient to keep track of all this during the parse time, but that seems like a lot of special
- // case handling for what it's worth.
- if cfg!(debug_assertions) {
- let relative_selector_anchor = selector.iter_raw_parse_order_from(0).next();
- debug_assert!(
- relative_selector_anchor.is_some(),
- "Relative selector is empty"
- );
- debug_assert!(
- matches!(
- relative_selector_anchor.unwrap(),
- Component::RelativeSelectorAnchor
- ),
- "Relative selector anchor is missing"
- );
- }
- // Leave a hint for narrowing down the search space when we're matching.
- let match_hint = match selector.combinator_at_parse_order(1) {
- Combinator::Descendant => RelativeSelectorMatchHint::InSubtree,
- Combinator::Child => {
- let composition = CombinatorComposition::for_relative_selector(&selector);
- if composition.is_empty() || composition == CombinatorComposition::SIBLINGS
- {
- RelativeSelectorMatchHint::InChild
- } else {
- // Technically, for any composition that consists of child combinators only,
- // the search space is depth-constrained, but it's probably not worth optimizing for.
- RelativeSelectorMatchHint::InSubtree
- }
- },
- Combinator::NextSibling => {
- let composition = CombinatorComposition::for_relative_selector(&selector);
- if composition.is_empty() {
- RelativeSelectorMatchHint::InNextSibling
- } else if composition == CombinatorComposition::SIBLINGS {
- RelativeSelectorMatchHint::InSibling
- } else if composition == CombinatorComposition::DESCENDANTS {
- // Match won't cross multiple siblings.
- RelativeSelectorMatchHint::InNextSiblingSubtree
- } else {
- RelativeSelectorMatchHint::InSiblingSubtree
- }
- },
- Combinator::LaterSibling => {
- let composition = CombinatorComposition::for_relative_selector(&selector);
- if composition.is_empty() || composition == CombinatorComposition::SIBLINGS
- {
- RelativeSelectorMatchHint::InSibling
- } else {
- // Even if the match may not cross multiple siblings, we have to look until
- // we find a match anyway.
- RelativeSelectorMatchHint::InSiblingSubtree
- }
- },
- Combinator::Part | Combinator::PseudoElement | Combinator::SlotAssignment => {
- debug_assert!(false, "Unexpected relative combinator");
- RelativeSelectorMatchHint::InSubtree
- },
- };
- RelativeSelector {
- match_hint,
- selector,
- }
- })
- .collect();
- vec.into_boxed_slice()
- }
-}
-
-/// A CSS simple selector or combinator. We store both in the same enum for
-/// optimal packing and cache performance, see [1].
-///
-/// [1] https://bugzilla.mozilla.org/show_bug.cgi?id=1357973
-#[derive(Clone, Eq, PartialEq, ToShmem)]
-#[shmem(no_bounds)]
-pub enum Component<Impl: SelectorImpl> {
- LocalName(LocalName<Impl>),
-
- ID(#[shmem(field_bound)] Impl::Identifier),
- Class(#[shmem(field_bound)] Impl::Identifier),
-
- AttributeInNoNamespaceExists {
- #[shmem(field_bound)]
- local_name: Impl::LocalName,
- local_name_lower: Impl::LocalName,
- },
- // Used only when local_name is already lowercase.
- AttributeInNoNamespace {
- local_name: Impl::LocalName,
- operator: AttrSelectorOperator,
- #[shmem(field_bound)]
- value: Impl::AttrValue,
- case_sensitivity: ParsedCaseSensitivity,
- },
- // Use a Box in the less common cases with more data to keep size_of::<Component>() small.
- AttributeOther(Box<AttrSelectorWithOptionalNamespace<Impl>>),
-
- ExplicitUniversalType,
- ExplicitAnyNamespace,
-
- ExplicitNoNamespace,
- DefaultNamespace(#[shmem(field_bound)] Impl::NamespaceUrl),
- Namespace(
- #[shmem(field_bound)] Impl::NamespacePrefix,
- #[shmem(field_bound)] Impl::NamespaceUrl,
- ),
-
- /// Pseudo-classes
- Negation(Box<[Selector<Impl>]>),
- Root,
- Empty,
- Scope,
- ParentSelector,
- Nth(NthSelectorData),
- NthOf(NthOfSelectorData<Impl>),
- NonTSPseudoClass(#[shmem(field_bound)] Impl::NonTSPseudoClass),
- /// The ::slotted() pseudo-element:
- ///
- /// https://drafts.csswg.org/css-scoping/#slotted-pseudo
- ///
- /// The selector here is a compound selector, that is, no combinators.
- ///
- /// NOTE(emilio): This should support a list of selectors, but as of this
- /// writing no other browser does, and that allows them to put ::slotted()
- /// in the rule hash, so we do that too.
- ///
- /// See https://github.com/w3c/csswg-drafts/issues/2158
- Slotted(Selector<Impl>),
- /// The `::part` pseudo-element.
- /// https://drafts.csswg.org/css-shadow-parts/#part
- Part(#[shmem(field_bound)] Box<[Impl::Identifier]>),
- /// The `:host` pseudo-class:
- ///
- /// https://drafts.csswg.org/css-scoping/#host-selector
- ///
- /// NOTE(emilio): This should support a list of selectors, but as of this
- /// writing no other browser does, and that allows them to put :host()
- /// in the rule hash, so we do that too.
- ///
- /// See https://github.com/w3c/csswg-drafts/issues/2158
- Host(Option<Selector<Impl>>),
- /// The `:where` pseudo-class.
- ///
- /// https://drafts.csswg.org/selectors/#zero-matches
- ///
- /// The inner argument is conceptually a SelectorList, but we move the
- /// selectors to the heap to keep Component small.
- Where(Box<[Selector<Impl>]>),
- /// The `:is` pseudo-class.
- ///
- /// https://drafts.csswg.org/selectors/#matches-pseudo
- ///
- /// Same comment as above re. the argument.
- Is(Box<[Selector<Impl>]>),
- /// The `:has` pseudo-class.
- ///
- /// https://drafts.csswg.org/selectors/#has-pseudo
- ///
- /// Same comment as above re. the argument.
- Has(Box<[RelativeSelector<Impl>]>),
- /// An implementation-dependent pseudo-element selector.
- PseudoElement(#[shmem(field_bound)] Impl::PseudoElement),
-
- Combinator(Combinator),
-
- /// Used only for relative selectors, which starts with a combinator
- /// (With an implied descendant combinator if not specified).
- ///
- /// https://drafts.csswg.org/selectors-4/#typedef-relative-selector
- RelativeSelectorAnchor,
-}
-
-impl<Impl: SelectorImpl> Component<Impl> {
- /// Returns true if this is a combinator.
- #[inline]
- pub fn is_combinator(&self) -> bool {
- matches!(*self, Component::Combinator(_))
- }
-
- /// Returns true if this is a :host() selector.
- #[inline]
- pub fn is_host(&self) -> bool {
- matches!(*self, Component::Host(..))
- }
-
- /// Returns the value as a combinator if applicable, None otherwise.
- pub fn as_combinator(&self) -> Option<Combinator> {
- match *self {
- Component::Combinator(c) => Some(c),
- _ => None,
- }
- }
-
- /// Whether this component is valid after a pseudo-element. Only intended
- /// for sanity-checking.
- pub fn maybe_allowed_after_pseudo_element(&self) -> bool {
- match *self {
- Component::NonTSPseudoClass(..) => true,
- Component::Negation(ref selectors) |
- Component::Is(ref selectors) |
- Component::Where(ref selectors) => selectors.iter().all(|selector| {
- selector
- .iter_raw_match_order()
- .all(|c| c.maybe_allowed_after_pseudo_element())
- }),
- _ => false,
- }
- }
-
- /// Whether a given selector should match for stateless pseudo-elements.
- ///
- /// This is a bit subtle: Only selectors that return true in
- /// `maybe_allowed_after_pseudo_element` should end up here, and
- /// `NonTSPseudoClass` never matches (as it is a stateless pseudo after
- /// all).
- fn matches_for_stateless_pseudo_element(&self) -> bool {
- debug_assert!(
- self.maybe_allowed_after_pseudo_element(),
- "Someone messed up pseudo-element parsing: {:?}",
- *self
- );
- match *self {
- Component::Negation(ref selectors) => !selectors.iter().all(|selector| {
- selector
- .iter_raw_match_order()
- .all(|c| c.matches_for_stateless_pseudo_element())
- }),
- Component::Is(ref selectors) | Component::Where(ref selectors) => {
- selectors.iter().any(|selector| {
- selector
- .iter_raw_match_order()
- .all(|c| c.matches_for_stateless_pseudo_element())
- })
- },
- _ => false,
- }
- }
-
- pub fn visit<V>(&self, visitor: &mut V) -> bool
- where
- V: SelectorVisitor<Impl = Impl>,
- {
- use self::Component::*;
- if !visitor.visit_simple_selector(self) {
- return false;
- }
-
- match *self {
- Slotted(ref selector) => {
- if !selector.visit(visitor) {
- return false;
- }
- },
- Host(Some(ref selector)) => {
- if !selector.visit(visitor) {
- return false;
- }
- },
- AttributeInNoNamespaceExists {
- ref local_name,
- ref local_name_lower,
- } => {
- if !visitor.visit_attribute_selector(
- &NamespaceConstraint::Specific(&namespace_empty_string::<Impl>()),
- local_name,
- local_name_lower,
- ) {
- return false;
- }
- },
- AttributeInNoNamespace { ref local_name, .. } => {
- if !visitor.visit_attribute_selector(
- &NamespaceConstraint::Specific(&namespace_empty_string::<Impl>()),
- local_name,
- local_name,
- ) {
- return false;
- }
- },
- AttributeOther(ref attr_selector) => {
- let empty_string;
- let namespace = match attr_selector.namespace() {
- Some(ns) => ns,
- None => {
- empty_string = crate::parser::namespace_empty_string::<Impl>();
- NamespaceConstraint::Specific(&empty_string)
- },
- };
- if !visitor.visit_attribute_selector(
- &namespace,
- &attr_selector.local_name,
- &attr_selector.local_name_lower,
- ) {
- return false;
- }
- },
-
- NonTSPseudoClass(ref pseudo_class) => {
- if !pseudo_class.visit(visitor) {
- return false;
- }
- },
- Negation(ref list) | Is(ref list) | Where(ref list) => {
- let list_kind = SelectorListKind::from_component(self);
- debug_assert!(!list_kind.is_empty());
- if !visitor.visit_selector_list(list_kind, &list) {
- return false;
- }
- },
- NthOf(ref nth_of_data) => {
- if !visitor.visit_selector_list(SelectorListKind::NTH_OF, nth_of_data.selectors()) {
- return false;
- }
- },
- _ => {},
- }
-
- true
- }
-}
-
-#[derive(Clone, Eq, PartialEq, ToShmem)]
-#[shmem(no_bounds)]
-pub struct LocalName<Impl: SelectorImpl> {
- #[shmem(field_bound)]
- pub name: Impl::LocalName,
- pub lower_name: Impl::LocalName,
-}
-
-impl<Impl: SelectorImpl> Debug for Selector<Impl> {
- fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
- f.write_str("Selector(")?;
- self.to_css(f)?;
- write!(
- f,
- ", specificity = {:#x}, flags = {:?})",
- self.specificity(),
- self.flags()
- )
- }
-}
-
-impl<Impl: SelectorImpl> Debug for Component<Impl> {
- fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
- self.to_css(f)
- }
-}
-impl<Impl: SelectorImpl> Debug for AttrSelectorWithOptionalNamespace<Impl> {
- fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
- self.to_css(f)
- }
-}
-impl<Impl: SelectorImpl> Debug for LocalName<Impl> {
- fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
- self.to_css(f)
- }
-}
-
-fn serialize_selector_list<'a, Impl, I, W>(iter: I, dest: &mut W) -> fmt::Result
-where
- Impl: SelectorImpl,
- I: Iterator<Item = &'a Selector<Impl>>,
- W: fmt::Write,
-{
- let mut first = true;
- for selector in iter {
- if !first {
- dest.write_str(", ")?;
- }
- first = false;
- selector.to_css(dest)?;
- }
- Ok(())
-}
-
-impl<Impl: SelectorImpl> ToCss for SelectorList<Impl> {
- fn to_css<W>(&self, dest: &mut W) -> fmt::Result
- where
- W: fmt::Write,
- {
- serialize_selector_list(self.0.iter(), dest)
- }
-}
-
-impl<Impl: SelectorImpl> ToCss for Selector<Impl> {
- fn to_css<W>(&self, dest: &mut W) -> fmt::Result
- where
- W: fmt::Write,
- {
- // Compound selectors invert the order of their contents, so we need to
- // undo that during serialization.
- //
- // This two-iterator strategy involves walking over the selector twice.
- // We could do something more clever, but selector serialization probably
- // isn't hot enough to justify it, and the stringification likely
- // dominates anyway.
- //
- // NB: A parse-order iterator is a Rev<>, which doesn't expose as_slice(),
- // which we need for |split|. So we split by combinators on a match-order
- // sequence and then reverse.
-
- let mut combinators = self
- .iter_raw_match_order()
- .rev()
- .filter_map(|x| x.as_combinator());
- let compound_selectors = self
- .iter_raw_match_order()
- .as_slice()
- .split(|x| x.is_combinator())
- .rev();
-
- let mut combinators_exhausted = false;
- for compound in compound_selectors {
- debug_assert!(!combinators_exhausted);
-
- // https://drafts.csswg.org/cssom/#serializing-selectors
- if compound.is_empty() {
- continue;
- }
- if let Component::RelativeSelectorAnchor = compound.first().unwrap() {
- debug_assert!(
- compound.len() == 1,
- "RelativeLeft should only be a simple selector"
- );
- combinators.next().unwrap().to_css_relative(dest)?;
- continue;
- }
-
- // 1. If there is only one simple selector in the compound selectors
- // which is a universal selector, append the result of
- // serializing the universal selector to s.
- //
- // Check if `!compound.empty()` first--this can happen if we have
- // something like `... > ::before`, because we store `>` and `::`
- // both as combinators internally.
- //
- // If we are in this case, after we have serialized the universal
- // selector, we skip Step 2 and continue with the algorithm.
- let (can_elide_namespace, first_non_namespace) = match compound[0] {
- Component::ExplicitAnyNamespace |
- Component::ExplicitNoNamespace |
- Component::Namespace(..) => (false, 1),
- Component::DefaultNamespace(..) => (true, 1),
- _ => (true, 0),
- };
- let mut perform_step_2 = true;
- let next_combinator = combinators.next();
- if first_non_namespace == compound.len() - 1 {
- match (next_combinator, &compound[first_non_namespace]) {
- // We have to be careful here, because if there is a
- // pseudo element "combinator" there isn't really just
- // the one simple selector. Technically this compound
- // selector contains the pseudo element selector as well
- // -- Combinator::PseudoElement, just like
- // Combinator::SlotAssignment, don't exist in the
- // spec.
- (Some(Combinator::PseudoElement), _) |
- (Some(Combinator::SlotAssignment), _) => (),
- (_, &Component::ExplicitUniversalType) => {
- // Iterate over everything so we serialize the namespace
- // too.
- for simple in compound.iter() {
- simple.to_css(dest)?;
- }
- // Skip step 2, which is an "otherwise".
- perform_step_2 = false;
- },
- _ => (),
- }
- }
-
- // 2. Otherwise, for each simple selector in the compound selectors
- // that is not a universal selector of which the namespace prefix
- // maps to a namespace that is not the default namespace
- // serialize the simple selector and append the result to s.
- //
- // See https://github.com/w3c/csswg-drafts/issues/1606, which is
- // proposing to change this to match up with the behavior asserted
- // in cssom/serialize-namespaced-type-selectors.html, which the
- // following code tries to match.
- if perform_step_2 {
- for simple in compound.iter() {
- if let Component::ExplicitUniversalType = *simple {
- // Can't have a namespace followed by a pseudo-element
- // selector followed by a universal selector in the same
- // compound selector, so we don't have to worry about the
- // real namespace being in a different `compound`.
- if can_elide_namespace {
- continue;
- }
- }
- simple.to_css(dest)?;
- }
- }
-
- // 3. If this is not the last part of the chain of the selector
- // append a single SPACE (U+0020), followed by the combinator
- // ">", "+", "~", ">>", "||", as appropriate, followed by another
- // single SPACE (U+0020) if the combinator was not whitespace, to
- // s.
- match next_combinator {
- Some(c) => c.to_css(dest)?,
- None => combinators_exhausted = true,
- };
-
- // 4. If this is the last part of the chain of the selector and
- // there is a pseudo-element, append "::" followed by the name of
- // the pseudo-element, to s.
- //
- // (we handle this above)
- }
-
- Ok(())
- }
-}
-
-impl Combinator {
- fn to_css_internal<W>(&self, dest: &mut W, prefix_space: bool) -> fmt::Result
- where
- W: fmt::Write,
- {
- if matches!(
- *self,
- Combinator::PseudoElement | Combinator::Part | Combinator::SlotAssignment
- ) {
- return Ok(());
- }
- if prefix_space {
- dest.write_char(' ')?;
- }
- match *self {
- Combinator::Child => dest.write_str("> "),
- Combinator::Descendant => Ok(()),
- Combinator::NextSibling => dest.write_str("+ "),
- Combinator::LaterSibling => dest.write_str("~ "),
- Combinator::PseudoElement | Combinator::Part | Combinator::SlotAssignment => unsafe {
- debug_unreachable!("Already handled")
- },
- }
- }
-
- fn to_css_relative<W>(&self, dest: &mut W) -> fmt::Result
- where
- W: fmt::Write,
- {
- self.to_css_internal(dest, false)
- }
-}
-
-impl ToCss for Combinator {
- fn to_css<W>(&self, dest: &mut W) -> fmt::Result
- where
- W: fmt::Write,
- {
- self.to_css_internal(dest, true)
- }
-}
-
-impl<Impl: SelectorImpl> ToCss for Component<Impl> {
- fn to_css<W>(&self, dest: &mut W) -> fmt::Result
- where
- W: fmt::Write,
- {
- use self::Component::*;
-
- match *self {
- Combinator(ref c) => c.to_css(dest),
- Slotted(ref selector) => {
- dest.write_str("::slotted(")?;
- selector.to_css(dest)?;
- dest.write_char(')')
- },
- Part(ref part_names) => {
- dest.write_str("::part(")?;
- for (i, name) in part_names.iter().enumerate() {
- if i != 0 {
- dest.write_char(' ')?;
- }
- name.to_css(dest)?;
- }
- dest.write_char(')')
- },
- PseudoElement(ref p) => p.to_css(dest),
- ID(ref s) => {
- dest.write_char('#')?;
- s.to_css(dest)
- },
- Class(ref s) => {
- dest.write_char('.')?;
- s.to_css(dest)
- },
- LocalName(ref s) => s.to_css(dest),
- ExplicitUniversalType => dest.write_char('*'),
-
- DefaultNamespace(_) => Ok(()),
- ExplicitNoNamespace => dest.write_char('|'),
- ExplicitAnyNamespace => dest.write_str("*|"),
- Namespace(ref prefix, _) => {
- prefix.to_css(dest)?;
- dest.write_char('|')
- },
-
- AttributeInNoNamespaceExists { ref local_name, .. } => {
- dest.write_char('[')?;
- local_name.to_css(dest)?;
- dest.write_char(']')
- },
- AttributeInNoNamespace {
- ref local_name,
- operator,
- ref value,
- case_sensitivity,
- ..
- } => {
- dest.write_char('[')?;
- local_name.to_css(dest)?;
- operator.to_css(dest)?;
- dest.write_char('"')?;
- value.to_css(dest)?;
- dest.write_char('"')?;
- match case_sensitivity {
- ParsedCaseSensitivity::CaseSensitive |
- ParsedCaseSensitivity::AsciiCaseInsensitiveIfInHtmlElementInHtmlDocument => {},
- ParsedCaseSensitivity::AsciiCaseInsensitive => dest.write_str(" i")?,
- ParsedCaseSensitivity::ExplicitCaseSensitive => dest.write_str(" s")?,
- }
- dest.write_char(']')
- },
- AttributeOther(ref attr_selector) => attr_selector.to_css(dest),
-
- // Pseudo-classes
- Root => dest.write_str(":root"),
- Empty => dest.write_str(":empty"),
- Scope => dest.write_str(":scope"),
- ParentSelector => dest.write_char('&'),
- Host(ref selector) => {
- dest.write_str(":host")?;
- if let Some(ref selector) = *selector {
- dest.write_char('(')?;
- selector.to_css(dest)?;
- dest.write_char(')')?;
- }
- Ok(())
- },
- Nth(ref nth_data) => {
- nth_data.write_start(dest)?;
- if nth_data.is_function {
- nth_data.write_affine(dest)?;
- dest.write_char(')')?;
- }
- Ok(())
- },
- NthOf(ref nth_of_data) => {
- let nth_data = nth_of_data.nth_data();
- nth_data.write_start(dest)?;
- debug_assert!(
- nth_data.is_function,
- "A selector must be a function to hold An+B notation"
- );
- nth_data.write_affine(dest)?;
- debug_assert!(
- matches!(nth_data.ty, NthType::Child | NthType::LastChild),
- "Only :nth-child or :nth-last-child can be of a selector list"
- );
- debug_assert!(
- !nth_of_data.selectors().is_empty(),
- "The selector list should not be empty"
- );
- dest.write_str(" of ")?;
- serialize_selector_list(nth_of_data.selectors().iter(), dest)?;
- dest.write_char(')')
- },
- Is(ref list) | Where(ref list) | Negation(ref list) => {
- match *self {
- Where(..) => dest.write_str(":where(")?,
- Is(..) => dest.write_str(":is(")?,
- Negation(..) => dest.write_str(":not(")?,
- _ => unreachable!(),
- }
- serialize_selector_list(list.iter(), dest)?;
- dest.write_str(")")
- },
- Has(ref list) => {
- dest.write_str(":has(")?;
- let mut first = true;
- for RelativeSelector { ref selector, .. } in list.iter() {
- if !first {
- dest.write_str(", ")?;
- }
- first = false;
- selector.to_css(dest)?;
- }
- dest.write_str(")")
- },
- NonTSPseudoClass(ref pseudo) => pseudo.to_css(dest),
- RelativeSelectorAnchor => Ok(()),
- }
- }
-}
-
-impl<Impl: SelectorImpl> ToCss for AttrSelectorWithOptionalNamespace<Impl> {
- fn to_css<W>(&self, dest: &mut W) -> fmt::Result
- where
- W: fmt::Write,
- {
- dest.write_char('[')?;
- match self.namespace {
- Some(NamespaceConstraint::Specific((ref prefix, _))) => {
- prefix.to_css(dest)?;
- dest.write_char('|')?
- },
- Some(NamespaceConstraint::Any) => dest.write_str("*|")?,
- None => {},
- }
- self.local_name.to_css(dest)?;
- match self.operation {
- ParsedAttrSelectorOperation::Exists => {},
- ParsedAttrSelectorOperation::WithValue {
- operator,
- case_sensitivity,
- ref value,
- } => {
- operator.to_css(dest)?;
- dest.write_char('"')?;
- value.to_css(dest)?;
- dest.write_char('"')?;
- match case_sensitivity {
- ParsedCaseSensitivity::CaseSensitive |
- ParsedCaseSensitivity::AsciiCaseInsensitiveIfInHtmlElementInHtmlDocument => {},
- ParsedCaseSensitivity::AsciiCaseInsensitive => dest.write_str(" i")?,
- ParsedCaseSensitivity::ExplicitCaseSensitive => dest.write_str(" s")?,
- }
- },
- }
- dest.write_char(']')
- }
-}
-
-impl<Impl: SelectorImpl> ToCss for LocalName<Impl> {
- fn to_css<W>(&self, dest: &mut W) -> fmt::Result
- where
- W: fmt::Write,
- {
- self.name.to_css(dest)
- }
-}
-
-/// Build up a Selector.
-/// selector : simple_selector_sequence [ combinator simple_selector_sequence ]* ;
-///
-/// `Err` means invalid selector.
-fn parse_selector<'i, 't, P, Impl>(
- parser: &P,
- input: &mut CssParser<'i, 't>,
- mut state: SelectorParsingState,
- parse_relative: ParseRelative,
-) -> Result<Selector<Impl>, ParseError<'i, P::Error>>
-where
- P: Parser<'i, Impl = Impl>,
- Impl: SelectorImpl,
-{
- let mut builder = SelectorBuilder::default();
- if parse_relative == ParseRelative::Yes {
- builder.push_simple_selector(Component::RelativeSelectorAnchor);
- // Do we see a combinator? If so, push that. Otherwise, push a descendant combinator.
- builder
- .push_combinator(parse_combinator::<P, Impl>(input).unwrap_or(Combinator::Descendant));
- }
- 'outer_loop: loop {
- // Parse a sequence of simple selectors.
- let empty = parse_compound_selector(parser, &mut state, input, &mut builder)?;
- if empty {
- return Err(input.new_custom_error(if builder.has_combinators() {
- SelectorParseErrorKind::DanglingCombinator
- } else {
- SelectorParseErrorKind::EmptySelector
- }));
- }
-
- if state.intersects(SelectorParsingState::AFTER_PSEUDO) {
- debug_assert!(state.intersects(
- SelectorParsingState::AFTER_PSEUDO_ELEMENT |
- SelectorParsingState::AFTER_SLOTTED |
- SelectorParsingState::AFTER_PART
- ));
- break;
- }
-
- let combinator = if let Ok(c) = parse_combinator::<P, Impl>(input) {
- c
- } else {
- break 'outer_loop;
- };
-
- if !state.allows_combinators() {
- return Err(input.new_custom_error(SelectorParseErrorKind::InvalidState));
- }
-
- builder.push_combinator(combinator);
- }
- return Ok(Selector(builder.build()));
-}
-
-fn parse_combinator<'i, 't, P, Impl>(input: &mut CssParser<'i, 't>) -> Result<Combinator, ()> {
- let mut any_whitespace = false;
- loop {
- let before_this_token = input.state();
- match input.next_including_whitespace() {
- Err(_e) => return Err(()),
- Ok(&Token::WhiteSpace(_)) => any_whitespace = true,
- Ok(&Token::Delim('>')) => {
- return Ok(Combinator::Child);
- },
- Ok(&Token::Delim('+')) => {
- return Ok(Combinator::NextSibling);
- },
- Ok(&Token::Delim('~')) => {
- return Ok(Combinator::LaterSibling);
- },
- Ok(_) => {
- input.reset(&before_this_token);
- if any_whitespace {
- return Ok(Combinator::Descendant);
- } else {
- return Err(());
- }
- },
- }
- }
-}
-
-impl<Impl: SelectorImpl> Selector<Impl> {
- /// Parse a selector, without any pseudo-element.
- #[inline]
- pub fn parse<'i, 't, P>(
- parser: &P,
- input: &mut CssParser<'i, 't>,
- ) -> Result<Self, ParseError<'i, P::Error>>
- where
- P: Parser<'i, Impl = Impl>,
- {
- parse_selector(
- parser,
- input,
- SelectorParsingState::empty(),
- ParseRelative::No,
- )
- }
-}
-
-/// * `Err(())`: Invalid selector, abort
-/// * `Ok(false)`: Not a type selector, could be something else. `input` was not consumed.
-/// * `Ok(true)`: Length 0 (`*|*`), 1 (`*|E` or `ns|*`) or 2 (`|E` or `ns|E`)
-fn parse_type_selector<'i, 't, P, Impl, S>(
- parser: &P,
- input: &mut CssParser<'i, 't>,
- state: SelectorParsingState,
- sink: &mut S,
-) -> Result<bool, ParseError<'i, P::Error>>
-where
- P: Parser<'i, Impl = Impl>,
- Impl: SelectorImpl,
- S: Push<Component<Impl>>,
-{
- match parse_qualified_name(parser, input, /* in_attr_selector = */ false) {
- Err(ParseError {
- kind: ParseErrorKind::Basic(BasicParseErrorKind::EndOfInput),
- ..
- }) |
- Ok(OptionalQName::None(_)) => Ok(false),
- Ok(OptionalQName::Some(namespace, local_name)) => {
- if state.intersects(SelectorParsingState::AFTER_PSEUDO) {
- return Err(input.new_custom_error(SelectorParseErrorKind::InvalidState));
- }
- match namespace {
- QNamePrefix::ImplicitAnyNamespace => {},
- QNamePrefix::ImplicitDefaultNamespace(url) => {
- sink.push(Component::DefaultNamespace(url))
- },
- QNamePrefix::ExplicitNamespace(prefix, url) => {
- sink.push(match parser.default_namespace() {
- Some(ref default_url) if url == *default_url => {
- Component::DefaultNamespace(url)
- },
- _ => Component::Namespace(prefix, url),
- })
- },
- QNamePrefix::ExplicitNoNamespace => sink.push(Component::ExplicitNoNamespace),
- QNamePrefix::ExplicitAnyNamespace => {
- match parser.default_namespace() {
- // Element type selectors that have no namespace
- // component (no namespace separator) represent elements
- // without regard to the element's namespace (equivalent
- // to "*|") unless a default namespace has been declared
- // for namespaced selectors (e.g. in CSS, in the style
- // sheet). If a default namespace has been declared,
- // such selectors will represent only elements in the
- // default namespace.
- // -- Selectors § 6.1.1
- // So we'll have this act the same as the
- // QNamePrefix::ImplicitAnyNamespace case.
- None => {},
- Some(_) => sink.push(Component::ExplicitAnyNamespace),
- }
- },
- QNamePrefix::ImplicitNoNamespace => {
- unreachable!() // Not returned with in_attr_selector = false
- },
- }
- match local_name {
- Some(name) => sink.push(Component::LocalName(LocalName {
- lower_name: to_ascii_lowercase(&name).as_ref().into(),
- name: name.as_ref().into(),
- })),
- None => sink.push(Component::ExplicitUniversalType),
- }
- Ok(true)
- },
- Err(e) => Err(e),
- }
-}
-
-#[derive(Debug)]
-enum SimpleSelectorParseResult<Impl: SelectorImpl> {
- SimpleSelector(Component<Impl>),
- PseudoElement(Impl::PseudoElement),
- SlottedPseudo(Selector<Impl>),
- PartPseudo(Box<[Impl::Identifier]>),
-}
-
-#[derive(Debug)]
-enum QNamePrefix<Impl: SelectorImpl> {
- ImplicitNoNamespace, // `foo` in attr selectors
- ImplicitAnyNamespace, // `foo` in type selectors, without a default ns
- ImplicitDefaultNamespace(Impl::NamespaceUrl), // `foo` in type selectors, with a default ns
- ExplicitNoNamespace, // `|foo`
- ExplicitAnyNamespace, // `*|foo`
- ExplicitNamespace(Impl::NamespacePrefix, Impl::NamespaceUrl), // `prefix|foo`
-}
-
-enum OptionalQName<'i, Impl: SelectorImpl> {
- Some(QNamePrefix<Impl>, Option<CowRcStr<'i>>),
- None(Token<'i>),
-}
-
-/// * `Err(())`: Invalid selector, abort
-/// * `Ok(None(token))`: Not a simple selector, could be something else. `input` was not consumed,
-/// but the token is still returned.
-/// * `Ok(Some(namespace, local_name))`: `None` for the local name means a `*` universal selector
-fn parse_qualified_name<'i, 't, P, Impl>(
- parser: &P,
- input: &mut CssParser<'i, 't>,
- in_attr_selector: bool,
-) -> Result<OptionalQName<'i, Impl>, ParseError<'i, P::Error>>
-where
- P: Parser<'i, Impl = Impl>,
- Impl: SelectorImpl,
-{
- let default_namespace = |local_name| {
- let namespace = match parser.default_namespace() {
- Some(url) => QNamePrefix::ImplicitDefaultNamespace(url),
- None => QNamePrefix::ImplicitAnyNamespace,
- };
- Ok(OptionalQName::Some(namespace, local_name))
- };
-
- let explicit_namespace = |input: &mut CssParser<'i, 't>, namespace| {
- let location = input.current_source_location();
- match input.next_including_whitespace() {
- Ok(&Token::Delim('*')) if !in_attr_selector => Ok(OptionalQName::Some(namespace, None)),
- Ok(&Token::Ident(ref local_name)) => {
- Ok(OptionalQName::Some(namespace, Some(local_name.clone())))
- },
- Ok(t) if in_attr_selector => {
- let e = SelectorParseErrorKind::InvalidQualNameInAttr(t.clone());
- Err(location.new_custom_error(e))
- },
- Ok(t) => Err(location.new_custom_error(
- SelectorParseErrorKind::ExplicitNamespaceUnexpectedToken(t.clone()),
- )),
- Err(e) => Err(e.into()),
- }
- };
-
- let start = input.state();
- match input.next_including_whitespace() {
- Ok(Token::Ident(value)) => {
- let value = value.clone();
- let after_ident = input.state();
- match input.next_including_whitespace() {
- Ok(&Token::Delim('|')) => {
- let prefix = value.as_ref().into();
- let result = parser.namespace_for_prefix(&prefix);
- let url = result.ok_or(
- after_ident
- .source_location()
- .new_custom_error(SelectorParseErrorKind::ExpectedNamespace(value)),
- )?;
- explicit_namespace(input, QNamePrefix::ExplicitNamespace(prefix, url))
- },
- _ => {
- input.reset(&after_ident);
- if in_attr_selector {
- Ok(OptionalQName::Some(
- QNamePrefix::ImplicitNoNamespace,
- Some(value),
- ))
- } else {
- default_namespace(Some(value))
- }
- },
- }
- },
- Ok(Token::Delim('*')) => {
- let after_star = input.state();
- match input.next_including_whitespace() {
- Ok(&Token::Delim('|')) => {
- explicit_namespace(input, QNamePrefix::ExplicitAnyNamespace)
- },
- _ if !in_attr_selector => {
- input.reset(&after_star);
- default_namespace(None)
- },
- result => {
- let t = result?;
- Err(after_star
- .source_location()
- .new_custom_error(SelectorParseErrorKind::ExpectedBarInAttr(t.clone())))
- },
- }
- },
- Ok(Token::Delim('|')) => explicit_namespace(input, QNamePrefix::ExplicitNoNamespace),
- Ok(t) => {
- let t = t.clone();
- input.reset(&start);
- Ok(OptionalQName::None(t))
- },
- Err(e) => {
- input.reset(&start);
- Err(e.into())
- },
- }
-}
-
-fn parse_attribute_selector<'i, 't, P, Impl>(
- parser: &P,
- input: &mut CssParser<'i, 't>,
-) -> Result<Component<Impl>, ParseError<'i, P::Error>>
-where
- P: Parser<'i, Impl = Impl>,
- Impl: SelectorImpl,
-{
- let namespace;
- let local_name;
-
- input.skip_whitespace();
-
- match parse_qualified_name(parser, input, /* in_attr_selector = */ true)? {
- OptionalQName::None(t) => {
- return Err(input.new_custom_error(
- SelectorParseErrorKind::NoQualifiedNameInAttributeSelector(t),
- ));
- },
- OptionalQName::Some(_, None) => unreachable!(),
- OptionalQName::Some(ns, Some(ln)) => {
- local_name = ln;
- namespace = match ns {
- QNamePrefix::ImplicitNoNamespace | QNamePrefix::ExplicitNoNamespace => None,
- QNamePrefix::ExplicitNamespace(prefix, url) => {
- Some(NamespaceConstraint::Specific((prefix, url)))
- },
- QNamePrefix::ExplicitAnyNamespace => Some(NamespaceConstraint::Any),
- QNamePrefix::ImplicitAnyNamespace | QNamePrefix::ImplicitDefaultNamespace(_) => {
- unreachable!() // Not returned with in_attr_selector = true
- },
- }
- },
- }
-
- let location = input.current_source_location();
- let operator = match input.next() {
- // [foo]
- Err(_) => {
- let local_name_lower = to_ascii_lowercase(&local_name).as_ref().into();
- let local_name = local_name.as_ref().into();
- if let Some(namespace) = namespace {
- return Ok(Component::AttributeOther(Box::new(
- AttrSelectorWithOptionalNamespace {
- namespace: Some(namespace),
- local_name,
- local_name_lower,
- operation: ParsedAttrSelectorOperation::Exists,
- },
- )));
- } else {
- return Ok(Component::AttributeInNoNamespaceExists {
- local_name,
- local_name_lower,
- });
- }
- },
-
- // [foo=bar]
- Ok(&Token::Delim('=')) => AttrSelectorOperator::Equal,
- // [foo~=bar]
- Ok(&Token::IncludeMatch) => AttrSelectorOperator::Includes,
- // [foo|=bar]
- Ok(&Token::DashMatch) => AttrSelectorOperator::DashMatch,
- // [foo^=bar]
- Ok(&Token::PrefixMatch) => AttrSelectorOperator::Prefix,
- // [foo*=bar]
- Ok(&Token::SubstringMatch) => AttrSelectorOperator::Substring,
- // [foo$=bar]
- Ok(&Token::SuffixMatch) => AttrSelectorOperator::Suffix,
- Ok(t) => {
- return Err(location.new_custom_error(
- SelectorParseErrorKind::UnexpectedTokenInAttributeSelector(t.clone()),
- ));
- },
- };
-
- let value = match input.expect_ident_or_string() {
- Ok(t) => t.clone(),
- Err(BasicParseError {
- kind: BasicParseErrorKind::UnexpectedToken(t),
- location,
- }) => return Err(location.new_custom_error(SelectorParseErrorKind::BadValueInAttr(t))),
- Err(e) => return Err(e.into()),
- };
-
- let attribute_flags = parse_attribute_flags(input)?;
- let value = value.as_ref().into();
- let local_name_lower;
- let local_name_is_ascii_lowercase;
- let case_sensitivity;
- {
- let local_name_lower_cow = to_ascii_lowercase(&local_name);
- case_sensitivity =
- attribute_flags.to_case_sensitivity(local_name_lower_cow.as_ref(), namespace.is_some());
- local_name_lower = local_name_lower_cow.as_ref().into();
- local_name_is_ascii_lowercase = matches!(local_name_lower_cow, Cow::Borrowed(..));
- }
- let local_name = local_name.as_ref().into();
- if namespace.is_some() || !local_name_is_ascii_lowercase {
- Ok(Component::AttributeOther(Box::new(
- AttrSelectorWithOptionalNamespace {
- namespace,
- local_name,
- local_name_lower,
- operation: ParsedAttrSelectorOperation::WithValue {
- operator,
- case_sensitivity,
- value,
- },
- },
- )))
- } else {
- Ok(Component::AttributeInNoNamespace {
- local_name,
- operator,
- value,
- case_sensitivity,
- })
- }
-}
-
-/// An attribute selector can have 's' or 'i' as flags, or no flags at all.
-enum AttributeFlags {
- // Matching should be case-sensitive ('s' flag).
- CaseSensitive,
- // Matching should be case-insensitive ('i' flag).
- AsciiCaseInsensitive,
- // No flags. Matching behavior depends on the name of the attribute.
- CaseSensitivityDependsOnName,
-}
-
-impl AttributeFlags {
- fn to_case_sensitivity(self, local_name: &str, have_namespace: bool) -> ParsedCaseSensitivity {
- match self {
- AttributeFlags::CaseSensitive => ParsedCaseSensitivity::ExplicitCaseSensitive,
- AttributeFlags::AsciiCaseInsensitive => ParsedCaseSensitivity::AsciiCaseInsensitive,
- AttributeFlags::CaseSensitivityDependsOnName => {
- if !have_namespace &&
- include!(concat!(
- env!("OUT_DIR"),
- "/ascii_case_insensitive_html_attributes.rs"
- ))
- .contains(local_name)
- {
- ParsedCaseSensitivity::AsciiCaseInsensitiveIfInHtmlElementInHtmlDocument
- } else {
- ParsedCaseSensitivity::CaseSensitive
- }
- },
- }
- }
-}
-
-fn parse_attribute_flags<'i, 't>(
- input: &mut CssParser<'i, 't>,
-) -> Result<AttributeFlags, BasicParseError<'i>> {
- let location = input.current_source_location();
- let token = match input.next() {
- Ok(t) => t,
- Err(..) => {
- // Selectors spec says language-defined; HTML says it depends on the
- // exact attribute name.
- return Ok(AttributeFlags::CaseSensitivityDependsOnName);
- },
- };
-
- let ident = match *token {
- Token::Ident(ref i) => i,
- ref other => return Err(location.new_basic_unexpected_token_error(other.clone())),
- };
-
- Ok(match_ignore_ascii_case! {
- ident,
- "i" => AttributeFlags::AsciiCaseInsensitive,
- "s" => AttributeFlags::CaseSensitive,
- _ => return Err(location.new_basic_unexpected_token_error(token.clone())),
- })
-}
-
-/// Level 3: Parse **one** simple_selector. (Though we might insert a second
-/// implied "<defaultns>|*" type selector.)
-fn parse_negation<'i, 't, P, Impl>(
- parser: &P,
- input: &mut CssParser<'i, 't>,
- state: SelectorParsingState,
-) -> Result<Component<Impl>, ParseError<'i, P::Error>>
-where
- P: Parser<'i, Impl = Impl>,
- Impl: SelectorImpl,
-{
- let list = SelectorList::parse_with_state(
- parser,
- input,
- state |
- SelectorParsingState::SKIP_DEFAULT_NAMESPACE |
- SelectorParsingState::DISALLOW_PSEUDOS,
- ForgivingParsing::No,
- ParseRelative::No,
- )?;
-
- Ok(Component::Negation(list.0.into_vec().into_boxed_slice()))
-}
-
-/// simple_selector_sequence
-/// : [ type_selector | universal ] [ HASH | class | attrib | pseudo | negation ]*
-/// | [ HASH | class | attrib | pseudo | negation ]+
-///
-/// `Err(())` means invalid selector.
-/// `Ok(true)` is an empty selector
-fn parse_compound_selector<'i, 't, P, Impl>(
- parser: &P,
- state: &mut SelectorParsingState,
- input: &mut CssParser<'i, 't>,
- builder: &mut SelectorBuilder<Impl>,
-) -> Result<bool, ParseError<'i, P::Error>>
-where
- P: Parser<'i, Impl = Impl>,
- Impl: SelectorImpl,
-{
- input.skip_whitespace();
-
- let mut empty = true;
- if parse_type_selector(parser, input, *state, builder)? {
- empty = false;
- }
-
- loop {
- let result = match parse_one_simple_selector(parser, input, *state)? {
- None => break,
- Some(result) => result,
- };
-
- if empty {
- if let Some(url) = parser.default_namespace() {
- // If there was no explicit type selector, but there is a
- // default namespace, there is an implicit "<defaultns>|*" type
- // selector. Except for :host() or :not() / :is() / :where(),
- // where we ignore it.
- //
- // https://drafts.csswg.org/css-scoping/#host-element-in-tree:
- //
- // When considered within its own shadow trees, the shadow
- // host is featureless. Only the :host, :host(), and
- // :host-context() pseudo-classes are allowed to match it.
- //
- // https://drafts.csswg.org/selectors-4/#featureless:
- //
- // A featureless element does not match any selector at all,
- // except those it is explicitly defined to match. If a
- // given selector is allowed to match a featureless element,
- // it must do so while ignoring the default namespace.
- //
- // https://drafts.csswg.org/selectors-4/#matches
- //
- // Default namespace declarations do not affect the compound
- // selector representing the subject of any selector within
- // a :is() pseudo-class, unless that compound selector
- // contains an explicit universal selector or type selector.
- //
- // (Similar quotes for :where() / :not())
- //
- let ignore_default_ns = state
- .intersects(SelectorParsingState::SKIP_DEFAULT_NAMESPACE) ||
- matches!(
- result,
- SimpleSelectorParseResult::SimpleSelector(Component::Host(..))
- );
- if !ignore_default_ns {
- builder.push_simple_selector(Component::DefaultNamespace(url));
- }
- }
- }
-
- empty = false;
-
- match result {
- SimpleSelectorParseResult::SimpleSelector(s) => {
- builder.push_simple_selector(s);
- },
- SimpleSelectorParseResult::PartPseudo(part_names) => {
- state.insert(SelectorParsingState::AFTER_PART);
- builder.push_combinator(Combinator::Part);
- builder.push_simple_selector(Component::Part(part_names));
- },
- SimpleSelectorParseResult::SlottedPseudo(selector) => {
- state.insert(SelectorParsingState::AFTER_SLOTTED);
- builder.push_combinator(Combinator::SlotAssignment);
- builder.push_simple_selector(Component::Slotted(selector));
- },
- SimpleSelectorParseResult::PseudoElement(p) => {
- state.insert(SelectorParsingState::AFTER_PSEUDO_ELEMENT);
- if !p.accepts_state_pseudo_classes() {
- state.insert(SelectorParsingState::AFTER_NON_STATEFUL_PSEUDO_ELEMENT);
- }
- builder.push_combinator(Combinator::PseudoElement);
- builder.push_simple_selector(Component::PseudoElement(p));
- },
- }
- }
- Ok(empty)
-}
-
-fn parse_is_where<'i, 't, P, Impl>(
- parser: &P,
- input: &mut CssParser<'i, 't>,
- state: SelectorParsingState,
- component: impl FnOnce(Box<[Selector<Impl>]>) -> Component<Impl>,
-) -> Result<Component<Impl>, ParseError<'i, P::Error>>
-where
- P: Parser<'i, Impl = Impl>,
- Impl: SelectorImpl,
-{
- debug_assert!(parser.parse_is_and_where());
- // https://drafts.csswg.org/selectors/#matches-pseudo:
- //
- // Pseudo-elements cannot be represented by the matches-any
- // pseudo-class; they are not valid within :is().
- //
- let inner = SelectorList::parse_with_state(
- parser,
- input,
- state |
- SelectorParsingState::SKIP_DEFAULT_NAMESPACE |
- SelectorParsingState::DISALLOW_PSEUDOS,
- ForgivingParsing::Yes,
- ParseRelative::No,
- )?;
- Ok(component(inner.0.into_vec().into_boxed_slice()))
-}
-
-fn parse_has<'i, 't, P, Impl>(
- parser: &P,
- input: &mut CssParser<'i, 't>,
- state: SelectorParsingState,
-) -> Result<Component<Impl>, ParseError<'i, P::Error>>
-where
- P: Parser<'i, Impl = Impl>,
- Impl: SelectorImpl,
-{
- debug_assert!(parser.parse_has());
- if state.intersects(SelectorParsingState::DISALLOW_RELATIVE_SELECTOR) {
- return Err(input.new_custom_error(SelectorParseErrorKind::InvalidState));
- }
- // Nested `:has()` is disallowed, mark it as such.
- // Note: The spec defines ":has-allowed pseudo-element," but there's no
- // pseudo-element defined as such at the moment.
- // https://w3c.github.io/csswg-drafts/selectors-4/#has-allowed-pseudo-element
- let inner = SelectorList::parse_with_state(
- parser,
- input,
- state |
- SelectorParsingState::SKIP_DEFAULT_NAMESPACE |
- SelectorParsingState::DISALLOW_PSEUDOS |
- SelectorParsingState::DISALLOW_RELATIVE_SELECTOR,
- ForgivingParsing::No,
- ParseRelative::Yes,
- )?;
- Ok(Component::Has(RelativeSelector::from_selector_list(inner)))
-}
-
-fn parse_functional_pseudo_class<'i, 't, P, Impl>(
- parser: &P,
- input: &mut CssParser<'i, 't>,
- name: CowRcStr<'i>,
- state: SelectorParsingState,
-) -> Result<Component<Impl>, ParseError<'i, P::Error>>
-where
- P: Parser<'i, Impl = Impl>,
- Impl: SelectorImpl,
-{
- match_ignore_ascii_case! { &name,
- "nth-child" => return parse_nth_pseudo_class(parser, input, state, NthType::Child),
- "nth-of-type" => return parse_nth_pseudo_class(parser, input, state, NthType::OfType),
- "nth-last-child" => return parse_nth_pseudo_class(parser, input, state, NthType::LastChild),
- "nth-last-of-type" => return parse_nth_pseudo_class(parser, input, state, NthType::LastOfType),
- "is" if parser.parse_is_and_where() => return parse_is_where(parser, input, state, Component::Is),
- "where" if parser.parse_is_and_where() => return parse_is_where(parser, input, state, Component::Where),
- "has" if parser.parse_has() => return parse_has(parser, input, state),
- "host" => {
- if !state.allows_tree_structural_pseudo_classes() {
- return Err(input.new_custom_error(SelectorParseErrorKind::InvalidState));
- }
- return Ok(Component::Host(Some(parse_inner_compound_selector(parser, input, state)?)));
- },
- "not" => {
- return parse_negation(parser, input, state)
- },
- _ => {}
- }
-
- if parser.parse_is_and_where() && parser.is_is_alias(&name) {
- return parse_is_where(parser, input, state, Component::Is);
- }
-
- if !state.allows_custom_functional_pseudo_classes() {
- return Err(input.new_custom_error(SelectorParseErrorKind::InvalidState));
- }
-
- P::parse_non_ts_functional_pseudo_class(parser, name, input).map(Component::NonTSPseudoClass)
-}
-
-fn parse_nth_pseudo_class<'i, 't, P, Impl>(
- parser: &P,
- input: &mut CssParser<'i, 't>,
- state: SelectorParsingState,
- ty: NthType,
-) -> Result<Component<Impl>, ParseError<'i, P::Error>>
-where
- P: Parser<'i, Impl = Impl>,
- Impl: SelectorImpl,
-{
- if !state.allows_tree_structural_pseudo_classes() {
- return Err(input.new_custom_error(SelectorParseErrorKind::InvalidState));
- }
- let (a, b) = parse_nth(input)?;
- let nth_data = NthSelectorData {
- ty,
- is_function: true,
- a,
- b,
- };
- if !parser.parse_nth_child_of() || ty.is_of_type() {
- return Ok(Component::Nth(nth_data));
- }
-
- // Try to parse "of <selector-list>".
- if input.try_parse(|i| i.expect_ident_matching("of")).is_err() {
- return Ok(Component::Nth(nth_data));
- }
- // Whitespace between "of" and the selector list is optional
- // https://github.com/w3c/csswg-drafts/issues/8285
- let mut selectors = SelectorList::parse_with_state(
- parser,
- input,
- state |
- SelectorParsingState::SKIP_DEFAULT_NAMESPACE |
- SelectorParsingState::DISALLOW_PSEUDOS,
- ForgivingParsing::No,
- ParseRelative::No,
- )?;
- Ok(Component::NthOf(NthOfSelectorData::new(
- &nth_data,
- selectors.0.drain(..),
- )))
-}
-
-/// Returns whether the name corresponds to a CSS2 pseudo-element that
-/// can be specified with the single colon syntax (in addition to the
-/// double-colon syntax, which can be used for all pseudo-elements).
-fn is_css2_pseudo_element(name: &str) -> bool {
- // ** Do not add to this list! **
- match_ignore_ascii_case! { name,
- "before" | "after" | "first-line" | "first-letter" => true,
- _ => false,
- }
-}
-
-/// Parse a simple selector other than a type selector.
-///
-/// * `Err(())`: Invalid selector, abort
-/// * `Ok(None)`: Not a simple selector, could be something else. `input` was not consumed.
-/// * `Ok(Some(_))`: Parsed a simple selector or pseudo-element
-fn parse_one_simple_selector<'i, 't, P, Impl>(
- parser: &P,
- input: &mut CssParser<'i, 't>,
- state: SelectorParsingState,
-) -> Result<Option<SimpleSelectorParseResult<Impl>>, ParseError<'i, P::Error>>
-where
- P: Parser<'i, Impl = Impl>,
- Impl: SelectorImpl,
-{
- let start = input.state();
- let token = match input.next_including_whitespace().map(|t| t.clone()) {
- Ok(t) => t,
- Err(..) => {
- input.reset(&start);
- return Ok(None);
- },
- };
-
- Ok(Some(match token {
- Token::IDHash(id) => {
- if state.intersects(SelectorParsingState::AFTER_PSEUDO) {
- return Err(input.new_custom_error(SelectorParseErrorKind::InvalidState));
- }
- let id = Component::ID(id.as_ref().into());
- SimpleSelectorParseResult::SimpleSelector(id)
- },
- Token::Delim(delim) if delim == '.' || (delim == '&' && parser.parse_parent_selector()) => {
- if state.intersects(SelectorParsingState::AFTER_PSEUDO) {
- return Err(input.new_custom_error(SelectorParseErrorKind::InvalidState));
- }
- let location = input.current_source_location();
- SimpleSelectorParseResult::SimpleSelector(if delim == '&' {
- Component::ParentSelector
- } else {
- let class = match *input.next_including_whitespace()? {
- Token::Ident(ref class) => class,
- ref t => {
- let e = SelectorParseErrorKind::ClassNeedsIdent(t.clone());
- return Err(location.new_custom_error(e));
- },
- };
- Component::Class(class.as_ref().into())
- })
- },
- Token::SquareBracketBlock => {
- if state.intersects(SelectorParsingState::AFTER_PSEUDO) {
- return Err(input.new_custom_error(SelectorParseErrorKind::InvalidState));
- }
- let attr = input.parse_nested_block(|input| parse_attribute_selector(parser, input))?;
- SimpleSelectorParseResult::SimpleSelector(attr)
- },
- Token::Colon => {
- let location = input.current_source_location();
- let (is_single_colon, next_token) = match input.next_including_whitespace()?.clone() {
- Token::Colon => (false, input.next_including_whitespace()?.clone()),
- t => (true, t),
- };
- let (name, is_functional) = match next_token {
- Token::Ident(name) => (name, false),
- Token::Function(name) => (name, true),
- t => {
- let e = SelectorParseErrorKind::PseudoElementExpectedIdent(t);
- return Err(input.new_custom_error(e));
- },
- };
- let is_pseudo_element = !is_single_colon || is_css2_pseudo_element(&name);
- if is_pseudo_element {
- if !state.allows_pseudos() {
- return Err(input.new_custom_error(SelectorParseErrorKind::InvalidState));
- }
- let pseudo_element = if is_functional {
- if P::parse_part(parser) && name.eq_ignore_ascii_case("part") {
- if !state.allows_part() {
- return Err(
- input.new_custom_error(SelectorParseErrorKind::InvalidState)
- );
- }
- let names = input.parse_nested_block(|input| {
- let mut result = Vec::with_capacity(1);
- result.push(input.expect_ident()?.as_ref().into());
- while !input.is_exhausted() {
- result.push(input.expect_ident()?.as_ref().into());
- }
- Ok(result.into_boxed_slice())
- })?;
- return Ok(Some(SimpleSelectorParseResult::PartPseudo(names)));
- }
- if P::parse_slotted(parser) && name.eq_ignore_ascii_case("slotted") {
- if !state.allows_slotted() {
- return Err(
- input.new_custom_error(SelectorParseErrorKind::InvalidState)
- );
- }
- let selector = input.parse_nested_block(|input| {
- parse_inner_compound_selector(parser, input, state)
- })?;
- return Ok(Some(SimpleSelectorParseResult::SlottedPseudo(selector)));
- }
- input.parse_nested_block(|input| {
- P::parse_functional_pseudo_element(parser, name, input)
- })?
- } else {
- P::parse_pseudo_element(parser, location, name)?
- };
-
- if state.intersects(SelectorParsingState::AFTER_SLOTTED) &&
- !pseudo_element.valid_after_slotted()
- {
- return Err(input.new_custom_error(SelectorParseErrorKind::InvalidState));
- }
- SimpleSelectorParseResult::PseudoElement(pseudo_element)
- } else {
- let pseudo_class = if is_functional {
- input.parse_nested_block(|input| {
- parse_functional_pseudo_class(parser, input, name, state)
- })?
- } else {
- parse_simple_pseudo_class(parser, location, name, state)?
- };
- SimpleSelectorParseResult::SimpleSelector(pseudo_class)
- }
- },
- _ => {
- input.reset(&start);
- return Ok(None);
- },
- }))
-}
-
-fn parse_simple_pseudo_class<'i, P, Impl>(
- parser: &P,
- location: SourceLocation,
- name: CowRcStr<'i>,
- state: SelectorParsingState,
-) -> Result<Component<Impl>, ParseError<'i, P::Error>>
-where
- P: Parser<'i, Impl = Impl>,
- Impl: SelectorImpl,
-{
- if !state.allows_non_functional_pseudo_classes() {
- return Err(location.new_custom_error(SelectorParseErrorKind::InvalidState));
- }
-
- if state.allows_tree_structural_pseudo_classes() {
- match_ignore_ascii_case! { &name,
- "first-child" => return Ok(Component::Nth(NthSelectorData::first(/* of_type = */ false))),
- "last-child" => return Ok(Component::Nth(NthSelectorData::last(/* of_type = */ false))),
- "only-child" => return Ok(Component::Nth(NthSelectorData::only(/* of_type = */ false))),
- "root" => return Ok(Component::Root),
- "empty" => return Ok(Component::Empty),
- "scope" => return Ok(Component::Scope),
- "host" if P::parse_host(parser) => return Ok(Component::Host(None)),
- "first-of-type" => return Ok(Component::Nth(NthSelectorData::first(/* of_type = */ true))),
- "last-of-type" => return Ok(Component::Nth(NthSelectorData::last(/* of_type = */ true))),
- "only-of-type" => return Ok(Component::Nth(NthSelectorData::only(/* of_type = */ true))),
- _ => {},
- }
- }
-
- let pseudo_class = P::parse_non_ts_pseudo_class(parser, location, name)?;
- if state.intersects(SelectorParsingState::AFTER_PSEUDO_ELEMENT) &&
- !pseudo_class.is_user_action_state()
- {
- return Err(location.new_custom_error(SelectorParseErrorKind::InvalidState));
- }
- Ok(Component::NonTSPseudoClass(pseudo_class))
-}
-
-// NB: pub module in order to access the DummyParser
-#[cfg(test)]
-pub mod tests {
- use super::*;
- use crate::builder::SelectorFlags;
- use crate::parser;
- use cssparser::{serialize_identifier, Parser as CssParser, ParserInput, ToCss};
- use std::collections::HashMap;
- use std::fmt;
-
- #[derive(Clone, Debug, Eq, PartialEq)]
- pub enum PseudoClass {
- Hover,
- Active,
- Lang(String),
- }
-
- #[derive(Clone, Debug, Eq, PartialEq)]
- pub enum PseudoElement {
- Before,
- After,
- Highlight(String),
- }
-
- impl parser::PseudoElement for PseudoElement {
- type Impl = DummySelectorImpl;
-
- fn accepts_state_pseudo_classes(&self) -> bool {
- true
- }
-
- fn valid_after_slotted(&self) -> bool {
- true
- }
- }
-
- impl parser::NonTSPseudoClass for PseudoClass {
- type Impl = DummySelectorImpl;
-
- #[inline]
- fn is_active_or_hover(&self) -> bool {
- matches!(*self, PseudoClass::Active | PseudoClass::Hover)
- }
-
- #[inline]
- fn is_user_action_state(&self) -> bool {
- self.is_active_or_hover()
- }
- }
-
- impl ToCss for PseudoClass {
- fn to_css<W>(&self, dest: &mut W) -> fmt::Result
- where
- W: fmt::Write,
- {
- match *self {
- PseudoClass::Hover => dest.write_str(":hover"),
- PseudoClass::Active => dest.write_str(":active"),
- PseudoClass::Lang(ref lang) => {
- dest.write_str(":lang(")?;
- serialize_identifier(lang, dest)?;
- dest.write_char(')')
- },
- }
- }
- }
-
- impl ToCss for PseudoElement {
- fn to_css<W>(&self, dest: &mut W) -> fmt::Result
- where
- W: fmt::Write,
- {
- match *self {
- PseudoElement::Before => dest.write_str("::before"),
- PseudoElement::After => dest.write_str("::after"),
- PseudoElement::Highlight(ref name) => {
- dest.write_str("::highlight(")?;
- serialize_identifier(&name, dest)?;
- dest.write_char(')')
- },
- }
- }
- }
-
- #[derive(Clone, Debug, PartialEq)]
- pub struct DummySelectorImpl;
-
- #[derive(Default)]
- pub struct DummyParser {
- default_ns: Option<DummyAtom>,
- ns_prefixes: HashMap<DummyAtom, DummyAtom>,
- }
-
- impl DummyParser {
- fn default_with_namespace(default_ns: DummyAtom) -> DummyParser {
- DummyParser {
- default_ns: Some(default_ns),
- ns_prefixes: Default::default(),
- }
- }
- }
-
- impl SelectorImpl for DummySelectorImpl {
- type ExtraMatchingData<'a> = std::marker::PhantomData<&'a ()>;
- type AttrValue = DummyAttrValue;
- type Identifier = DummyAtom;
- type LocalName = DummyAtom;
- type NamespaceUrl = DummyAtom;
- type NamespacePrefix = DummyAtom;
- type BorrowedLocalName = DummyAtom;
- type BorrowedNamespaceUrl = DummyAtom;
- type NonTSPseudoClass = PseudoClass;
- type PseudoElement = PseudoElement;
- }
-
- #[derive(Clone, Debug, Default, Eq, Hash, PartialEq)]
- pub struct DummyAttrValue(String);
-
- impl ToCss for DummyAttrValue {
- fn to_css<W>(&self, dest: &mut W) -> fmt::Result
- where
- W: fmt::Write,
- {
- use std::fmt::Write;
-
- write!(cssparser::CssStringWriter::new(dest), "{}", &self.0)
- }
- }
-
- impl<'a> From<&'a str> for DummyAttrValue {
- fn from(string: &'a str) -> Self {
- Self(string.into())
- }
- }
-
- #[derive(Clone, Debug, Default, Eq, Hash, PartialEq)]
- pub struct DummyAtom(String);
-
- impl ToCss for DummyAtom {
- fn to_css<W>(&self, dest: &mut W) -> fmt::Result
- where
- W: fmt::Write,
- {
- serialize_identifier(&self.0, dest)
- }
- }
-
- impl From<String> for DummyAtom {
- fn from(string: String) -> Self {
- DummyAtom(string)
- }
- }
-
- impl<'a> From<&'a str> for DummyAtom {
- fn from(string: &'a str) -> Self {
- DummyAtom(string.into())
- }
- }
-
- impl<'i> Parser<'i> for DummyParser {
- type Impl = DummySelectorImpl;
- type Error = SelectorParseErrorKind<'i>;
-
- fn parse_slotted(&self) -> bool {
- true
- }
-
- fn parse_nth_child_of(&self) -> bool {
- true
- }
-
- fn parse_is_and_where(&self) -> bool {
- true
- }
-
- fn parse_has(&self) -> bool {
- true
- }
-
- fn parse_parent_selector(&self) -> bool {
- true
- }
-
- fn parse_part(&self) -> bool {
- true
- }
-
- fn parse_non_ts_pseudo_class(
- &self,
- location: SourceLocation,
- name: CowRcStr<'i>,
- ) -> Result<PseudoClass, SelectorParseError<'i>> {
- match_ignore_ascii_case! { &name,
- "hover" => return Ok(PseudoClass::Hover),
- "active" => return Ok(PseudoClass::Active),
- _ => {}
- }
- Err(
- location.new_custom_error(SelectorParseErrorKind::UnsupportedPseudoClassOrElement(
- name,
- )),
- )
- }
-
- fn parse_non_ts_functional_pseudo_class<'t>(
- &self,
- name: CowRcStr<'i>,
- parser: &mut CssParser<'i, 't>,
- ) -> Result<PseudoClass, SelectorParseError<'i>> {
- match_ignore_ascii_case! { &name,
- "lang" => {
- let lang = parser.expect_ident_or_string()?.as_ref().to_owned();
- return Ok(PseudoClass::Lang(lang));
- },
- _ => {}
- }
- Err(
- parser.new_custom_error(SelectorParseErrorKind::UnsupportedPseudoClassOrElement(
- name,
- )),
- )
- }
-
- fn parse_pseudo_element(
- &self,
- location: SourceLocation,
- name: CowRcStr<'i>,
- ) -> Result<PseudoElement, SelectorParseError<'i>> {
- match_ignore_ascii_case! { &name,
- "before" => return Ok(PseudoElement::Before),
- "after" => return Ok(PseudoElement::After),
- _ => {}
- }
- Err(
- location.new_custom_error(SelectorParseErrorKind::UnsupportedPseudoClassOrElement(
- name,
- )),
- )
- }
-
- fn parse_functional_pseudo_element<'t>(
- &self,
- name: CowRcStr<'i>,
- parser: &mut CssParser<'i, 't>,
- ) -> Result<PseudoElement, SelectorParseError<'i>> {
- match_ignore_ascii_case! {&name,
- "highlight" => return Ok(PseudoElement::Highlight(parser.expect_ident()?.as_ref().to_owned())),
- _ => {}
- }
- Err(
- parser.new_custom_error(SelectorParseErrorKind::UnsupportedPseudoClassOrElement(
- name,
- )),
- )
- }
-
- fn default_namespace(&self) -> Option<DummyAtom> {
- self.default_ns.clone()
- }
-
- fn namespace_for_prefix(&self, prefix: &DummyAtom) -> Option<DummyAtom> {
- self.ns_prefixes.get(prefix).cloned()
- }
- }
-
- fn parse<'i>(
- input: &'i str,
- ) -> Result<SelectorList<DummySelectorImpl>, SelectorParseError<'i>> {
- parse_ns(input, &DummyParser::default())
- }
-
- fn parse_expected<'i, 'a>(
- input: &'i str,
- expected: Option<&'a str>,
- ) -> Result<SelectorList<DummySelectorImpl>, SelectorParseError<'i>> {
- parse_ns_expected(input, &DummyParser::default(), expected)
- }
-
- fn parse_ns<'i>(
- input: &'i str,
- parser: &DummyParser,
- ) -> Result<SelectorList<DummySelectorImpl>, SelectorParseError<'i>> {
- parse_ns_expected(input, parser, None)
- }
-
- fn parse_ns_expected<'i, 'a>(
- input: &'i str,
- parser: &DummyParser,
- expected: Option<&'a str>,
- ) -> Result<SelectorList<DummySelectorImpl>, SelectorParseError<'i>> {
- let mut parser_input = ParserInput::new(input);
- let result = SelectorList::parse(parser, &mut CssParser::new(&mut parser_input));
- if let Ok(ref selectors) = result {
- // We can't assume that the serialized parsed selector will equal
- // the input; for example, if there is no default namespace, '*|foo'
- // should serialize to 'foo'.
- assert_eq!(
- selectors.to_css_string(),
- match expected {
- Some(x) => x,
- None => input,
- }
- );
- }
- result
- }
-
- fn specificity(a: u32, b: u32, c: u32) -> u32 {
- a << 20 | b << 10 | c
- }
-
- #[test]
- fn test_empty() {
- let mut input = ParserInput::new(":empty");
- let list = SelectorList::parse(&DummyParser::default(), &mut CssParser::new(&mut input));
- assert!(list.is_ok());
- }
-
- const MATHML: &str = "http://www.w3.org/1998/Math/MathML";
- const SVG: &str = "http://www.w3.org/2000/svg";
-
- #[test]
- fn test_parsing() {
- assert!(parse("").is_err());
- assert!(parse(":lang(4)").is_err());
- assert!(parse(":lang(en US)").is_err());
- assert_eq!(
- parse("EeÉ"),
- Ok(SelectorList::from_vec(vec![Selector::from_vec(
- vec![Component::LocalName(LocalName {
- name: DummyAtom::from("EeÉ"),
- lower_name: DummyAtom::from("eeÉ"),
- })],
- specificity(0, 0, 1),
- Default::default(),
- )]))
- );
- assert_eq!(
- parse("|e"),
- Ok(SelectorList::from_vec(vec![Selector::from_vec(
- vec![
- Component::ExplicitNoNamespace,
- Component::LocalName(LocalName {
- name: DummyAtom::from("e"),
- lower_name: DummyAtom::from("e"),
- }),
- ],
- specificity(0, 0, 1),
- Default::default(),
- )]))
- );
- // When the default namespace is not set, *| should be elided.
- // https://github.com/servo/servo/pull/17537
- assert_eq!(
- parse_expected("*|e", Some("e")),
- Ok(SelectorList::from_vec(vec![Selector::from_vec(
- vec![Component::LocalName(LocalName {
- name: DummyAtom::from("e"),
- lower_name: DummyAtom::from("e"),
- })],
- specificity(0, 0, 1),
- Default::default(),
- )]))
- );
- // When the default namespace is set, *| should _not_ be elided (as foo
- // is no longer equivalent to *|foo--the former is only for foo in the
- // default namespace).
- // https://github.com/servo/servo/issues/16020
- assert_eq!(
- parse_ns(
- "*|e",
- &DummyParser::default_with_namespace(DummyAtom::from("https://mozilla.org"))
- ),
- Ok(SelectorList::from_vec(vec![Selector::from_vec(
- vec![
- Component::ExplicitAnyNamespace,
- Component::LocalName(LocalName {
- name: DummyAtom::from("e"),
- lower_name: DummyAtom::from("e"),
- }),
- ],
- specificity(0, 0, 1),
- Default::default(),
- )]))
- );
- assert_eq!(
- parse("*"),
- Ok(SelectorList::from_vec(vec![Selector::from_vec(
- vec![Component::ExplicitUniversalType],
- specificity(0, 0, 0),
- Default::default(),
- )]))
- );
- assert_eq!(
- parse("|*"),
- Ok(SelectorList::from_vec(vec![Selector::from_vec(
- vec![
- Component::ExplicitNoNamespace,
- Component::ExplicitUniversalType,
- ],
- specificity(0, 0, 0),
- Default::default(),
- )]))
- );
- assert_eq!(
- parse_expected("*|*", Some("*")),
- Ok(SelectorList::from_vec(vec![Selector::from_vec(
- vec![Component::ExplicitUniversalType],
- specificity(0, 0, 0),
- Default::default(),
- )]))
- );
- assert_eq!(
- parse_ns(
- "*|*",
- &DummyParser::default_with_namespace(DummyAtom::from("https://mozilla.org"))
- ),
- Ok(SelectorList::from_vec(vec![Selector::from_vec(
- vec![
- Component::ExplicitAnyNamespace,
- Component::ExplicitUniversalType,
- ],
- specificity(0, 0, 0),
- Default::default(),
- )]))
- );
- assert_eq!(
- parse(".foo:lang(en-US)"),
- Ok(SelectorList::from_vec(vec![Selector::from_vec(
- vec![
- Component::Class(DummyAtom::from("foo")),
- Component::NonTSPseudoClass(PseudoClass::Lang("en-US".to_owned())),
- ],
- specificity(0, 2, 0),
- Default::default(),
- )]))
- );
- assert_eq!(
- parse("#bar"),
- Ok(SelectorList::from_vec(vec![Selector::from_vec(
- vec![Component::ID(DummyAtom::from("bar"))],
- specificity(1, 0, 0),
- Default::default(),
- )]))
- );
- assert_eq!(
- parse("e.foo#bar"),
- Ok(SelectorList::from_vec(vec![Selector::from_vec(
- vec![
- Component::LocalName(LocalName {
- name: DummyAtom::from("e"),
- lower_name: DummyAtom::from("e"),
- }),
- Component::Class(DummyAtom::from("foo")),
- Component::ID(DummyAtom::from("bar")),
- ],
- specificity(1, 1, 1),
- Default::default(),
- )]))
- );
- assert_eq!(
- parse("e.foo #bar"),
- Ok(SelectorList::from_vec(vec![Selector::from_vec(
- vec![
- Component::LocalName(LocalName {
- name: DummyAtom::from("e"),
- lower_name: DummyAtom::from("e"),
- }),
- Component::Class(DummyAtom::from("foo")),
- Component::Combinator(Combinator::Descendant),
- Component::ID(DummyAtom::from("bar")),
- ],
- specificity(1, 1, 1),
- Default::default(),
- )]))
- );
- // Default namespace does not apply to attribute selectors
- // https://github.com/mozilla/servo/pull/1652
- let mut parser = DummyParser::default();
- assert_eq!(
- parse_ns("[Foo]", &parser),
- Ok(SelectorList::from_vec(vec![Selector::from_vec(
- vec![Component::AttributeInNoNamespaceExists {
- local_name: DummyAtom::from("Foo"),
- local_name_lower: DummyAtom::from("foo"),
- }],
- specificity(0, 1, 0),
- Default::default(),
- )]))
- );
- assert!(parse_ns("svg|circle", &parser).is_err());
- parser
- .ns_prefixes
- .insert(DummyAtom("svg".into()), DummyAtom(SVG.into()));
- assert_eq!(
- parse_ns("svg|circle", &parser),
- Ok(SelectorList::from_vec(vec![Selector::from_vec(
- vec![
- Component::Namespace(DummyAtom("svg".into()), SVG.into()),
- Component::LocalName(LocalName {
- name: DummyAtom::from("circle"),
- lower_name: DummyAtom::from("circle"),
- }),
- ],
- specificity(0, 0, 1),
- Default::default(),
- )]))
- );
- assert_eq!(
- parse_ns("svg|*", &parser),
- Ok(SelectorList::from_vec(vec![Selector::from_vec(
- vec![
- Component::Namespace(DummyAtom("svg".into()), SVG.into()),
- Component::ExplicitUniversalType,
- ],
- specificity(0, 0, 0),
- Default::default(),
- )]))
- );
- // Default namespace does not apply to attribute selectors
- // https://github.com/mozilla/servo/pull/1652
- // but it does apply to implicit type selectors
- // https://github.com/servo/rust-selectors/pull/82
- parser.default_ns = Some(MATHML.into());
- assert_eq!(
- parse_ns("[Foo]", &parser),
- Ok(SelectorList::from_vec(vec![Selector::from_vec(
- vec![
- Component::DefaultNamespace(MATHML.into()),
- Component::AttributeInNoNamespaceExists {
- local_name: DummyAtom::from("Foo"),
- local_name_lower: DummyAtom::from("foo"),
- },
- ],
- specificity(0, 1, 0),
- Default::default(),
- )]))
- );
- // Default namespace does apply to type selectors
- assert_eq!(
- parse_ns("e", &parser),
- Ok(SelectorList::from_vec(vec![Selector::from_vec(
- vec![
- Component::DefaultNamespace(MATHML.into()),
- Component::LocalName(LocalName {
- name: DummyAtom::from("e"),
- lower_name: DummyAtom::from("e"),
- }),
- ],
- specificity(0, 0, 1),
- Default::default(),
- )]))
- );
- assert_eq!(
- parse_ns("*", &parser),
- Ok(SelectorList::from_vec(vec![Selector::from_vec(
- vec![
- Component::DefaultNamespace(MATHML.into()),
- Component::ExplicitUniversalType,
- ],
- specificity(0, 0, 0),
- Default::default(),
- )]))
- );
- assert_eq!(
- parse_ns("*|*", &parser),
- Ok(SelectorList::from_vec(vec![Selector::from_vec(
- vec![
- Component::ExplicitAnyNamespace,
- Component::ExplicitUniversalType,
- ],
- specificity(0, 0, 0),
- Default::default(),
- )]))
- );
- // Default namespace applies to universal and type selectors inside :not and :matches,
- // but not otherwise.
- assert_eq!(
- parse_ns(":not(.cl)", &parser),
- Ok(SelectorList::from_vec(vec![Selector::from_vec(
- vec![
- Component::DefaultNamespace(MATHML.into()),
- Component::Negation(
- vec![Selector::from_vec(
- vec![Component::Class(DummyAtom::from("cl"))],
- specificity(0, 1, 0),
- Default::default(),
- )]
- .into_boxed_slice()
- ),
- ],
- specificity(0, 1, 0),
- Default::default(),
- )]))
- );
- assert_eq!(
- parse_ns(":not(*)", &parser),
- Ok(SelectorList::from_vec(vec![Selector::from_vec(
- vec![
- Component::DefaultNamespace(MATHML.into()),
- Component::Negation(
- vec![Selector::from_vec(
- vec![
- Component::DefaultNamespace(MATHML.into()),
- Component::ExplicitUniversalType,
- ],
- specificity(0, 0, 0),
- Default::default(),
- )]
- .into_boxed_slice(),
- ),
- ],
- specificity(0, 0, 0),
- Default::default(),
- )]))
- );
- assert_eq!(
- parse_ns(":not(e)", &parser),
- Ok(SelectorList::from_vec(vec![Selector::from_vec(
- vec![
- Component::DefaultNamespace(MATHML.into()),
- Component::Negation(
- vec![Selector::from_vec(
- vec![
- Component::DefaultNamespace(MATHML.into()),
- Component::LocalName(LocalName {
- name: DummyAtom::from("e"),
- lower_name: DummyAtom::from("e"),
- }),
- ],
- specificity(0, 0, 1),
- Default::default(),
- ),]
- .into_boxed_slice()
- ),
- ],
- specificity(0, 0, 1),
- Default::default(),
- )]))
- );
- assert_eq!(
- parse("[attr|=\"foo\"]"),
- Ok(SelectorList::from_vec(vec![Selector::from_vec(
- vec![Component::AttributeInNoNamespace {
- local_name: DummyAtom::from("attr"),
- operator: AttrSelectorOperator::DashMatch,
- value: DummyAttrValue::from("foo"),
- case_sensitivity: ParsedCaseSensitivity::CaseSensitive,
- }],
- specificity(0, 1, 0),
- Default::default(),
- )]))
- );
- // https://github.com/mozilla/servo/issues/1723
- assert_eq!(
- parse("::before"),
- Ok(SelectorList::from_vec(vec![Selector::from_vec(
- vec![
- Component::Combinator(Combinator::PseudoElement),
- Component::PseudoElement(PseudoElement::Before),
- ],
- specificity(0, 0, 1),
- SelectorFlags::HAS_PSEUDO,
- )]))
- );
- assert_eq!(
- parse("::before:hover"),
- Ok(SelectorList::from_vec(vec![Selector::from_vec(
- vec![
- Component::Combinator(Combinator::PseudoElement),
- Component::PseudoElement(PseudoElement::Before),
- Component::NonTSPseudoClass(PseudoClass::Hover),
- ],
- specificity(0, 1, 1),
- SelectorFlags::HAS_PSEUDO,
- )]))
- );
- assert_eq!(
- parse("::before:hover:hover"),
- Ok(SelectorList::from_vec(vec![Selector::from_vec(
- vec![
- Component::Combinator(Combinator::PseudoElement),
- Component::PseudoElement(PseudoElement::Before),
- Component::NonTSPseudoClass(PseudoClass::Hover),
- Component::NonTSPseudoClass(PseudoClass::Hover),
- ],
- specificity(0, 2, 1),
- SelectorFlags::HAS_PSEUDO,
- )]))
- );
- assert!(parse("::before:hover:lang(foo)").is_err());
- assert!(parse("::before:hover .foo").is_err());
- assert!(parse("::before .foo").is_err());
- assert!(parse("::before ~ bar").is_err());
- assert!(parse("::before:active").is_ok());
-
- // https://github.com/servo/servo/issues/15335
- assert!(parse(":: before").is_err());
- assert_eq!(
- parse("div ::after"),
- Ok(SelectorList::from_vec(vec![Selector::from_vec(
- vec![
- Component::LocalName(LocalName {
- name: DummyAtom::from("div"),
- lower_name: DummyAtom::from("div"),
- }),
- Component::Combinator(Combinator::Descendant),
- Component::Combinator(Combinator::PseudoElement),
- Component::PseudoElement(PseudoElement::After),
- ],
- specificity(0, 0, 2),
- SelectorFlags::HAS_PSEUDO,
- )]))
- );
- assert_eq!(
- parse("#d1 > .ok"),
- Ok(SelectorList::from_vec(vec![Selector::from_vec(
- vec![
- Component::ID(DummyAtom::from("d1")),
- Component::Combinator(Combinator::Child),
- Component::Class(DummyAtom::from("ok")),
- ],
- (1 << 20) + (1 << 10) + (0 << 0),
- Default::default(),
- )]))
- );
- parser.default_ns = None;
- assert!(parse(":not(#provel.old)").is_ok());
- assert!(parse(":not(#provel > old)").is_ok());
- assert!(parse("table[rules]:not([rules=\"none\"]):not([rules=\"\"])").is_ok());
- // https://github.com/servo/servo/issues/16017
- assert_eq!(
- parse_ns(":not(*)", &parser),
- Ok(SelectorList::from_vec(vec![Selector::from_vec(
- vec![Component::Negation(
- vec![Selector::from_vec(
- vec![Component::ExplicitUniversalType],
- specificity(0, 0, 0),
- Default::default(),
- )]
- .into_boxed_slice()
- )],
- specificity(0, 0, 0),
- Default::default(),
- )]))
- );
- assert_eq!(
- parse_ns(":not(|*)", &parser),
- Ok(SelectorList::from_vec(vec![Selector::from_vec(
- vec![Component::Negation(
- vec![Selector::from_vec(
- vec![
- Component::ExplicitNoNamespace,
- Component::ExplicitUniversalType,
- ],
- specificity(0, 0, 0),
- Default::default(),
- )]
- .into_boxed_slice(),
- )],
- specificity(0, 0, 0),
- Default::default(),
- )]))
- );
- // *| should be elided if there is no default namespace.
- // https://github.com/servo/servo/pull/17537
- assert_eq!(
- parse_ns_expected(":not(*|*)", &parser, Some(":not(*)")),
- Ok(SelectorList::from_vec(vec![Selector::from_vec(
- vec![Component::Negation(
- vec![Selector::from_vec(
- vec![Component::ExplicitUniversalType],
- specificity(0, 0, 0),
- Default::default()
- )]
- .into_boxed_slice()
- )],
- specificity(0, 0, 0),
- Default::default(),
- )]))
- );
-
- assert!(parse("::highlight(foo)").is_ok());
-
- assert!(parse("::slotted()").is_err());
- assert!(parse("::slotted(div)").is_ok());
- assert!(parse("::slotted(div).foo").is_err());
- assert!(parse("::slotted(div + bar)").is_err());
- assert!(parse("::slotted(div) + foo").is_err());
-
- assert!(parse("::part()").is_err());
- assert!(parse("::part(42)").is_err());
- assert!(parse("::part(foo bar)").is_ok());
- assert!(parse("::part(foo):hover").is_ok());
- assert!(parse("::part(foo) + bar").is_err());
-
- assert!(parse("div ::slotted(div)").is_ok());
- assert!(parse("div + slot::slotted(div)").is_ok());
- assert!(parse("div + slot::slotted(div.foo)").is_ok());
- assert!(parse("slot::slotted(div,foo)::first-line").is_err());
- assert!(parse("::slotted(div)::before").is_ok());
- assert!(parse("slot::slotted(div,foo)").is_err());
-
- assert!(parse("foo:where()").is_ok());
- assert!(parse("foo:where(div, foo, .bar baz)").is_ok());
- assert!(parse_expected("foo:where(::before)", Some("foo:where()")).is_ok());
- }
-
- #[test]
- fn parent_selector() {
- assert!(parse("foo &").is_ok());
- assert_eq!(
- parse("#foo &.bar"),
- Ok(SelectorList::from_vec(vec![Selector::from_vec(
- vec![
- Component::ID(DummyAtom::from("foo")),
- Component::Combinator(Combinator::Descendant),
- Component::ParentSelector,
- Component::Class(DummyAtom::from("bar")),
- ],
- (1 << 20) + (1 << 10) + (0 << 0),
- SelectorFlags::HAS_PARENT
- )]))
- );
-
- let parent = parse(".bar, div .baz").unwrap();
- let child = parse("#foo &.bar").unwrap();
- assert_eq!(
- SelectorList::from_vec(vec![child.0[0].replace_parent_selector(&parent.0)]),
- parse("#foo :is(.bar, div .baz).bar").unwrap()
- );
-
- let has_child = parse("#foo:has(&.bar)").unwrap();
- assert_eq!(
- SelectorList::from_vec(vec![has_child.0[0].replace_parent_selector(&parent.0)]),
- parse("#foo:has(:is(.bar, div .baz).bar)").unwrap()
- );
-
- let child = parse("#foo").unwrap();
- assert_eq!(
- SelectorList::from_vec(vec![child.0[0].replace_parent_selector(&parent.0)]),
- parse(":is(.bar, div .baz) #foo").unwrap()
- );
- }
-
- #[test]
- fn test_pseudo_iter() {
- let selector = &parse("q::before").unwrap().0[0];
- assert!(!selector.is_universal());
- let mut iter = selector.iter();
- assert_eq!(
- iter.next(),
- Some(&Component::PseudoElement(PseudoElement::Before))
- );
- assert_eq!(iter.next(), None);
- let combinator = iter.next_sequence();
- assert_eq!(combinator, Some(Combinator::PseudoElement));
- assert!(matches!(iter.next(), Some(&Component::LocalName(..))));
- assert_eq!(iter.next(), None);
- assert_eq!(iter.next_sequence(), None);
- }
-
- #[test]
- fn test_universal() {
- let selector = &parse_ns(
- "*|*::before",
- &DummyParser::default_with_namespace(DummyAtom::from("https://mozilla.org")),
- )
- .unwrap()
- .0[0];
- assert!(selector.is_universal());
- }
-
- #[test]
- fn test_empty_pseudo_iter() {
- let selector = &parse("::before").unwrap().0[0];
- assert!(selector.is_universal());
- let mut iter = selector.iter();
- assert_eq!(
- iter.next(),
- Some(&Component::PseudoElement(PseudoElement::Before))
- );
- assert_eq!(iter.next(), None);
- assert_eq!(iter.next_sequence(), Some(Combinator::PseudoElement));
- assert_eq!(iter.next(), None);
- assert_eq!(iter.next_sequence(), None);
- }
-
- struct TestVisitor {
- seen: Vec<String>,
- }
-
- impl SelectorVisitor for TestVisitor {
- type Impl = DummySelectorImpl;
-
- fn visit_simple_selector(&mut self, s: &Component<DummySelectorImpl>) -> bool {
- let mut dest = String::new();
- s.to_css(&mut dest).unwrap();
- self.seen.push(dest);
- true
- }
- }
-
- #[test]
- fn visitor() {
- let mut test_visitor = TestVisitor { seen: vec![] };
- parse(":not(:hover) ~ label").unwrap().0[0].visit(&mut test_visitor);
- assert!(test_visitor.seen.contains(&":hover".into()));
-
- let mut test_visitor = TestVisitor { seen: vec![] };
- parse("::before:hover").unwrap().0[0].visit(&mut test_visitor);
- assert!(test_visitor.seen.contains(&":hover".into()));
- }
-}
diff --git a/components/selectors/rustfmt.toml b/components/selectors/rustfmt.toml
deleted file mode 100644
index c7ad93bafe3..00000000000
--- a/components/selectors/rustfmt.toml
+++ /dev/null
@@ -1 +0,0 @@
-disable_all_formatting = true
diff --git a/components/selectors/sink.rs b/components/selectors/sink.rs
deleted file mode 100644
index dcdd7ff259d..00000000000
--- a/components/selectors/sink.rs
+++ /dev/null
@@ -1,31 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
-
-//! Small helpers to abstract over different containers.
-#![deny(missing_docs)]
-
-use smallvec::{Array, SmallVec};
-
-/// A trait to abstract over a `push` method that may be implemented for
-/// different kind of types.
-///
-/// Used to abstract over `Array`, `SmallVec` and `Vec`, and also to implement a
-/// type which `push` method does only tweak a byte when we only need to check
-/// for the presence of something.
-pub trait Push<T> {
- /// Push a value into self.
- fn push(&mut self, value: T);
-}
-
-impl<T> Push<T> for Vec<T> {
- fn push(&mut self, value: T) {
- Vec::push(self, value);
- }
-}
-
-impl<A: Array> Push<A::Item> for SmallVec<A> {
- fn push(&mut self, value: A::Item) {
- SmallVec::push(self, value);
- }
-}
diff --git a/components/selectors/tree.rs b/components/selectors/tree.rs
deleted file mode 100644
index 560b1e61d81..00000000000
--- a/components/selectors/tree.rs
+++ /dev/null
@@ -1,163 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
-
-//! Traits that nodes must implement. Breaks the otherwise-cyclic dependency
-//! between layout and style.
-
-use crate::attr::{AttrSelectorOperation, CaseSensitivity, NamespaceConstraint};
-use crate::matching::{ElementSelectorFlags, MatchingContext};
-use crate::parser::SelectorImpl;
-use std::fmt::Debug;
-use std::ptr::NonNull;
-
-/// Opaque representation of an Element, for identity comparisons.
-#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
-pub struct OpaqueElement(NonNull<()>);
-
-unsafe impl Send for OpaqueElement {}
-
-impl OpaqueElement {
- /// Creates a new OpaqueElement from an arbitrarily-typed pointer.
- pub fn new<T>(ptr: &T) -> Self {
- unsafe {
- OpaqueElement(NonNull::new_unchecked(
- ptr as *const T as *const () as *mut (),
- ))
- }
- }
-}
-
-pub trait Element: Sized + Clone + Debug {
- type Impl: SelectorImpl;
-
- /// Converts self into an opaque representation.
- fn opaque(&self) -> OpaqueElement;
-
- fn parent_element(&self) -> Option<Self>;
-
- /// Whether the parent node of this element is a shadow root.
- fn parent_node_is_shadow_root(&self) -> bool;
-
- /// The host of the containing shadow root, if any.
- fn containing_shadow_host(&self) -> Option<Self>;
-
- /// The parent of a given pseudo-element, after matching a pseudo-element
- /// selector.
- ///
- /// This is guaranteed to be called in a pseudo-element.
- fn pseudo_element_originating_element(&self) -> Option<Self> {
- debug_assert!(self.is_pseudo_element());
- self.parent_element()
- }
-
- /// Whether we're matching on a pseudo-element.
- fn is_pseudo_element(&self) -> bool;
-
- /// Skips non-element nodes
- fn prev_sibling_element(&self) -> Option<Self>;
-
- /// Skips non-element nodes
- fn next_sibling_element(&self) -> Option<Self>;
-
- /// Skips non-element nodes
- fn first_element_child(&self) -> Option<Self>;
-
- fn is_html_element_in_html_document(&self) -> bool;
-
- fn has_local_name(&self, local_name: &<Self::Impl as SelectorImpl>::BorrowedLocalName) -> bool;
-
- /// Empty string for no namespace
- fn has_namespace(&self, ns: &<Self::Impl as SelectorImpl>::BorrowedNamespaceUrl) -> bool;
-
- /// Whether this element and the `other` element have the same local name and namespace.
- fn is_same_type(&self, other: &Self) -> bool;
-
- fn attr_matches(
- &self,
- ns: &NamespaceConstraint<&<Self::Impl as SelectorImpl>::NamespaceUrl>,
- local_name: &<Self::Impl as SelectorImpl>::LocalName,
- operation: &AttrSelectorOperation<&<Self::Impl as SelectorImpl>::AttrValue>,
- ) -> bool;
-
- fn has_attr_in_no_namespace(
- &self,
- local_name: &<Self::Impl as SelectorImpl>::LocalName,
- ) -> bool {
- self.attr_matches(
- &NamespaceConstraint::Specific(&crate::parser::namespace_empty_string::<Self::Impl>()),
- local_name,
- &AttrSelectorOperation::Exists,
- )
- }
-
- fn match_non_ts_pseudo_class(
- &self,
- pc: &<Self::Impl as SelectorImpl>::NonTSPseudoClass,
- context: &mut MatchingContext<Self::Impl>,
- ) -> bool;
-
- fn match_pseudo_element(
- &self,
- pe: &<Self::Impl as SelectorImpl>::PseudoElement,
- context: &mut MatchingContext<Self::Impl>,
- ) -> bool;
-
- /// Sets selector flags on the elemnt itself or the parent, depending on the
- /// flags, which indicate what kind of work may need to be performed when
- /// DOM state changes.
- fn apply_selector_flags(&self, flags: ElementSelectorFlags);
-
- /// Whether this element is a `link`.
- fn is_link(&self) -> bool;
-
- /// Returns whether the element is an HTML <slot> element.
- fn is_html_slot_element(&self) -> bool;
-
- /// Returns the assigned <slot> element this element is assigned to.
- ///
- /// Necessary for the `::slotted` pseudo-class.
- fn assigned_slot(&self) -> Option<Self> {
- None
- }
-
- fn has_id(
- &self,
- id: &<Self::Impl as SelectorImpl>::Identifier,
- case_sensitivity: CaseSensitivity,
- ) -> bool;
-
- fn has_class(
- &self,
- name: &<Self::Impl as SelectorImpl>::Identifier,
- case_sensitivity: CaseSensitivity,
- ) -> bool;
-
- /// Returns the mapping from the `exportparts` attribute in the reverse
- /// direction, that is, in an outer-tree -> inner-tree direction.
- fn imported_part(
- &self,
- name: &<Self::Impl as SelectorImpl>::Identifier,
- ) -> Option<<Self::Impl as SelectorImpl>::Identifier>;
-
- fn is_part(&self, name: &<Self::Impl as SelectorImpl>::Identifier) -> bool;
-
- /// Returns whether this element matches `:empty`.
- ///
- /// That is, whether it does not contain any child element or any non-zero-length text node.
- /// See http://dev.w3.org/csswg/selectors-3/#empty-pseudo
- fn is_empty(&self) -> bool;
-
- /// Returns whether this element matches `:root`,
- /// i.e. whether it is the root element of a document.
- ///
- /// Note: this can be false even if `.parent_element()` is `None`
- /// if the parent node is a `DocumentFragment`.
- fn is_root(&self) -> bool;
-
- /// Returns whether this element should ignore matching nth child
- /// selector.
- fn ignores_nth_child_selectors(&self) -> bool {
- false
- }
-}
diff --git a/components/selectors/visitor.rs b/components/selectors/visitor.rs
deleted file mode 100644
index 785c12813a6..00000000000
--- a/components/selectors/visitor.rs
+++ /dev/null
@@ -1,111 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
-
-//! Visitor traits for selectors.
-
-#![deny(missing_docs)]
-
-use crate::attr::NamespaceConstraint;
-use crate::parser::{Combinator, Component, Selector, SelectorImpl};
-
-/// A trait to visit selector properties.
-///
-/// All the `visit_foo` methods return a boolean indicating whether the
-/// traversal should continue or not.
-pub trait SelectorVisitor: Sized {
- /// The selector implementation this visitor wants to visit.
- type Impl: SelectorImpl;
-
- /// Visit an attribute selector that may match (there are other selectors
- /// that may never match, like those containing whitespace or the empty
- /// string).
- fn visit_attribute_selector(
- &mut self,
- _namespace: &NamespaceConstraint<&<Self::Impl as SelectorImpl>::NamespaceUrl>,
- _local_name: &<Self::Impl as SelectorImpl>::LocalName,
- _local_name_lower: &<Self::Impl as SelectorImpl>::LocalName,
- ) -> bool {
- true
- }
-
- /// Visit a simple selector.
- fn visit_simple_selector(&mut self, _: &Component<Self::Impl>) -> bool {
- true
- }
-
- /// Visit a nested selector list. The caller is responsible to call visit
- /// into the internal selectors if / as needed.
- ///
- /// The default implementation does this.
- fn visit_selector_list(
- &mut self,
- _list_kind: SelectorListKind,
- list: &[Selector<Self::Impl>],
- ) -> bool {
- for nested in list {
- if !nested.visit(self) {
- return false;
- }
- }
- true
- }
-
- /// Visits a complex selector.
- ///
- /// Gets the combinator to the right of the selector, or `None` if the
- /// selector is the rightmost one.
- fn visit_complex_selector(&mut self, _combinator_to_right: Option<Combinator>) -> bool {
- true
- }
-}
-
-bitflags! {
- /// The kinds of components the visitor is visiting the selector list of, if any
- #[derive(Default)]
- pub struct SelectorListKind: u8 {
- /// The visitor is inside :not(..)
- const NEGATION = 1 << 0;
- /// The visitor is inside :is(..)
- const IS = 1 << 1;
- /// The visitor is inside :where(..)
- const WHERE = 1 << 2;
- /// The visitor is inside :nth-child(.. of <selector list>) or
- /// :nth-last-child(.. of <selector list>)
- const NTH_OF = 1 << 3;
- }
-}
-
-impl SelectorListKind {
- /// Construct a SelectorListKind for the corresponding component.
- pub fn from_component<Impl: SelectorImpl>(component: &Component<Impl>) -> Self {
- match component {
- Component::Negation(_) => SelectorListKind::NEGATION,
- Component::Is(_) => SelectorListKind::IS,
- Component::Where(_) => SelectorListKind::WHERE,
- Component::NthOf(_) => SelectorListKind::NTH_OF,
- _ => SelectorListKind::empty(),
- }
- }
-
- /// Whether the visitor is inside :not(..)
- pub fn in_negation(&self) -> bool {
- self.intersects(SelectorListKind::NEGATION)
- }
-
- /// Whether the visitor is inside :is(..)
- pub fn in_is(&self) -> bool {
- self.intersects(SelectorListKind::IS)
- }
-
- /// Whether the visitor is inside :where(..)
- pub fn in_where(&self) -> bool {
- self.intersects(SelectorListKind::WHERE)
- }
-
- /// Whether the visitor is inside :nth-child(.. of <selector list>) or
- /// :nth-last-child(.. of <selector list>)
- pub fn in_nth_of(&self) -> bool {
- self.intersects(SelectorListKind::NTH_OF)
- }
-}
diff --git a/components/servo/Cargo.toml b/components/servo/Cargo.toml
index 29ae33c0d5e..7fed4386d64 100644
--- a/components/servo/Cargo.toml
+++ b/components/servo/Cargo.toml
@@ -72,7 +72,7 @@ servo_config = { path = "../config" }
servo_geometry = { path = "../geometry" }
servo_url = { path = "../url" }
sparkle = { workspace = true }
-style = { path = "../style", features = ["servo"] }
+style = { workspace = true }
style_traits = { workspace = true }
surfman = { workspace = true }
webdriver_server = { path = "../webdriver_server", optional = true }
diff --git a/components/servo_arc/Cargo.toml b/components/servo_arc/Cargo.toml
deleted file mode 100644
index c975ad55412..00000000000
--- a/components/servo_arc/Cargo.toml
+++ /dev/null
@@ -1,20 +0,0 @@
-[package]
-name = "servo_arc"
-version = "0.2.0"
-authors = ["The Servo Project Developers"]
-license = "MIT OR Apache-2.0"
-repository = "https://github.com/servo/servo"
-description = "A fork of std::sync::Arc with some extra functionality and without weak references"
-
-[lib]
-name = "servo_arc"
-path = "lib.rs"
-
-[features]
-gecko_refcount_logging = []
-servo = ["serde"]
-
-[dependencies]
-nodrop = { version = "0.1.8" }
-serde = { workspace = true, optional = true }
-stable_deref_trait = "1.0.0"
diff --git a/components/servo_arc/LICENSE-APACHE b/components/servo_arc/LICENSE-APACHE
deleted file mode 100644
index 16fe87b06e8..00000000000
--- a/components/servo_arc/LICENSE-APACHE
+++ /dev/null
@@ -1,201 +0,0 @@
- Apache License
- Version 2.0, January 2004
- http://www.apache.org/licenses/
-
-TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
-
-1. Definitions.
-
- "License" shall mean the terms and conditions for use, reproduction,
- and distribution as defined by Sections 1 through 9 of this document.
-
- "Licensor" shall mean the copyright owner or entity authorized by
- the copyright owner that is granting the License.
-
- "Legal Entity" shall mean the union of the acting entity and all
- other entities that control, are controlled by, or are under common
- control with that entity. For the purposes of this definition,
- "control" means (i) the power, direct or indirect, to cause the
- direction or management of such entity, whether by contract or
- otherwise, or (ii) ownership of fifty percent (50%) or more of the
- outstanding shares, or (iii) beneficial ownership of such entity.
-
- "You" (or "Your") shall mean an individual or Legal Entity
- exercising permissions granted by this License.
-
- "Source" form shall mean the preferred form for making modifications,
- including but not limited to software source code, documentation
- source, and configuration files.
-
- "Object" form shall mean any form resulting from mechanical
- transformation or translation of a Source form, including but
- not limited to compiled object code, generated documentation,
- and conversions to other media types.
-
- "Work" shall mean the work of authorship, whether in Source or
- Object form, made available under the License, as indicated by a
- copyright notice that is included in or attached to the work
- (an example is provided in the Appendix below).
-
- "Derivative Works" shall mean any work, whether in Source or Object
- form, that is based on (or derived from) the Work and for which the
- editorial revisions, annotations, elaborations, or other modifications
- represent, as a whole, an original work of authorship. For the purposes
- of this License, Derivative Works shall not include works that remain
- separable from, or merely link (or bind by name) to the interfaces of,
- the Work and Derivative Works thereof.
-
- "Contribution" shall mean any work of authorship, including
- the original version of the Work and any modifications or additions
- to that Work or Derivative Works thereof, that is intentionally
- submitted to Licensor for inclusion in the Work by the copyright owner
- or by an individual or Legal Entity authorized to submit on behalf of
- the copyright owner. For the purposes of this definition, "submitted"
- means any form of electronic, verbal, or written communication sent
- to the Licensor or its representatives, including but not limited to
- communication on electronic mailing lists, source code control systems,
- and issue tracking systems that are managed by, or on behalf of, the
- Licensor for the purpose of discussing and improving the Work, but
- excluding communication that is conspicuously marked or otherwise
- designated in writing by the copyright owner as "Not a Contribution."
-
- "Contributor" shall mean Licensor and any individual or Legal Entity
- on behalf of whom a Contribution has been received by Licensor and
- subsequently incorporated within the Work.
-
-2. Grant of Copyright License. Subject to the terms and conditions of
- this License, each Contributor hereby grants to You a perpetual,
- worldwide, non-exclusive, no-charge, royalty-free, irrevocable
- copyright license to reproduce, prepare Derivative Works of,
- publicly display, publicly perform, sublicense, and distribute the
- Work and such Derivative Works in Source or Object form.
-
-3. Grant of Patent License. Subject to the terms and conditions of
- this License, each Contributor hereby grants to You a perpetual,
- worldwide, non-exclusive, no-charge, royalty-free, irrevocable
- (except as stated in this section) patent license to make, have made,
- use, offer to sell, sell, import, and otherwise transfer the Work,
- where such license applies only to those patent claims licensable
- by such Contributor that are necessarily infringed by their
- Contribution(s) alone or by combination of their Contribution(s)
- with the Work to which such Contribution(s) was submitted. If You
- institute patent litigation against any entity (including a
- cross-claim or counterclaim in a lawsuit) alleging that the Work
- or a Contribution incorporated within the Work constitutes direct
- or contributory patent infringement, then any patent licenses
- granted to You under this License for that Work shall terminate
- as of the date such litigation is filed.
-
-4. Redistribution. You may reproduce and distribute copies of the
- Work or Derivative Works thereof in any medium, with or without
- modifications, and in Source or Object form, provided that You
- meet the following conditions:
-
- (a) You must give any other recipients of the Work or
- Derivative Works a copy of this License; and
-
- (b) You must cause any modified files to carry prominent notices
- stating that You changed the files; and
-
- (c) You must retain, in the Source form of any Derivative Works
- that You distribute, all copyright, patent, trademark, and
- attribution notices from the Source form of the Work,
- excluding those notices that do not pertain to any part of
- the Derivative Works; and
-
- (d) If the Work includes a "NOTICE" text file as part of its
- distribution, then any Derivative Works that You distribute must
- include a readable copy of the attribution notices contained
- within such NOTICE file, excluding those notices that do not
- pertain to any part of the Derivative Works, in at least one
- of the following places: within a NOTICE text file distributed
- as part of the Derivative Works; within the Source form or
- documentation, if provided along with the Derivative Works; or,
- within a display generated by the Derivative Works, if and
- wherever such third-party notices normally appear. The contents
- of the NOTICE file are for informational purposes only and
- do not modify the License. You may add Your own attribution
- notices within Derivative Works that You distribute, alongside
- or as an addendum to the NOTICE text from the Work, provided
- that such additional attribution notices cannot be construed
- as modifying the License.
-
- You may add Your own copyright statement to Your modifications and
- may provide additional or different license terms and conditions
- for use, reproduction, or distribution of Your modifications, or
- for any such Derivative Works as a whole, provided Your use,
- reproduction, and distribution of the Work otherwise complies with
- the conditions stated in this License.
-
-5. Submission of Contributions. Unless You explicitly state otherwise,
- any Contribution intentionally submitted for inclusion in the Work
- by You to the Licensor shall be under the terms and conditions of
- this License, without any additional terms or conditions.
- Notwithstanding the above, nothing herein shall supersede or modify
- the terms of any separate license agreement you may have executed
- with Licensor regarding such Contributions.
-
-6. Trademarks. This License does not grant permission to use the trade
- names, trademarks, service marks, or product names of the Licensor,
- except as required for reasonable and customary use in describing the
- origin of the Work and reproducing the content of the NOTICE file.
-
-7. Disclaimer of Warranty. Unless required by applicable law or
- agreed to in writing, Licensor provides the Work (and each
- Contributor provides its Contributions) on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
- implied, including, without limitation, any warranties or conditions
- of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
- PARTICULAR PURPOSE. You are solely responsible for determining the
- appropriateness of using or redistributing the Work and assume any
- risks associated with Your exercise of permissions under this License.
-
-8. Limitation of Liability. In no event and under no legal theory,
- whether in tort (including negligence), contract, or otherwise,
- unless required by applicable law (such as deliberate and grossly
- negligent acts) or agreed to in writing, shall any Contributor be
- liable to You for damages, including any direct, indirect, special,
- incidental, or consequential damages of any character arising as a
- result of this License or out of the use or inability to use the
- Work (including but not limited to damages for loss of goodwill,
- work stoppage, computer failure or malfunction, or any and all
- other commercial damages or losses), even if such Contributor
- has been advised of the possibility of such damages.
-
-9. Accepting Warranty or Additional Liability. While redistributing
- the Work or Derivative Works thereof, You may choose to offer,
- and charge a fee for, acceptance of support, warranty, indemnity,
- or other liability obligations and/or rights consistent with this
- License. However, in accepting such obligations, You may act only
- on Your own behalf and on Your sole responsibility, not on behalf
- of any other Contributor, and only if You agree to indemnify,
- defend, and hold each Contributor harmless for any liability
- incurred by, or claims asserted against, such Contributor by reason
- of your accepting any such warranty or additional liability.
-
-END OF TERMS AND CONDITIONS
-
-APPENDIX: How to apply the Apache License to your work.
-
- To apply the Apache License to your work, attach the following
- boilerplate notice, with the fields enclosed by brackets "[]"
- replaced with your own identifying information. (Don't include
- the brackets!) The text should be enclosed in the appropriate
- comment syntax for the file format. We also recommend that a
- file or class name and description of purpose be included on the
- same "printed page" as the copyright notice for easier
- identification within third-party archives.
-
-Copyright [yyyy] [name of copyright owner]
-
-Licensed under the Apache License, Version 2.0 (the "License");
-you may not use this file except in compliance with the License.
-You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
-Unless required by applicable law or agreed to in writing, software
-distributed under the License is distributed on an "AS IS" BASIS,
-WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-See the License for the specific language governing permissions and
-limitations under the License.
diff --git a/components/servo_arc/LICENSE-MIT b/components/servo_arc/LICENSE-MIT
deleted file mode 100644
index 31aa79387f2..00000000000
--- a/components/servo_arc/LICENSE-MIT
+++ /dev/null
@@ -1,23 +0,0 @@
-Permission is hereby granted, free of charge, to any
-person obtaining a copy of this software and associated
-documentation files (the "Software"), to deal in the
-Software without restriction, including without
-limitation the rights to use, copy, modify, merge,
-publish, distribute, sublicense, and/or sell copies of
-the Software, and to permit persons to whom the Software
-is furnished to do so, subject to the following
-conditions:
-
-The above copyright notice and this permission notice
-shall be included in all copies or substantial portions
-of the Software.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
-ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
-TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
-PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
-SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
-CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
-OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR
-IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
-DEALINGS IN THE SOFTWARE.
diff --git a/components/servo_arc/lib.rs b/components/servo_arc/lib.rs
deleted file mode 100644
index cc71827283a..00000000000
--- a/components/servo_arc/lib.rs
+++ /dev/null
@@ -1,1370 +0,0 @@
-// Copyright 2012-2014 The Rust Project Developers. See the COPYRIGHT
-// file at the top-level directory of this distribution and at
-// http://rust-lang.org/COPYRIGHT.
-//
-// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
-// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
-// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
-// option. This file may not be copied, modified, or distributed
-// except according to those terms.
-
-//! Fork of Arc for Servo. This has the following advantages over std::sync::Arc:
-//!
-//! * We don't waste storage on the weak reference count.
-//! * We don't do extra RMU operations to handle the possibility of weak references.
-//! * We can experiment with arena allocation (todo).
-//! * We can add methods to support our custom use cases [1].
-//! * We have support for dynamically-sized types (see from_header_and_iter).
-//! * We have support for thin arcs to unsized types (see ThinArc).
-//! * We have support for references to static data, which don't do any
-//! refcounting.
-//!
-//! [1]: https://bugzilla.mozilla.org/show_bug.cgi?id=1360883
-
-// The semantics of `Arc` are already documented in the Rust docs, so we don't
-// duplicate those here.
-#![allow(missing_docs)]
-
-#[cfg(feature = "servo")]
-extern crate serde;
-extern crate stable_deref_trait;
-
-#[cfg(feature = "servo")]
-use serde::{Deserialize, Serialize};
-use stable_deref_trait::{CloneStableDeref, StableDeref};
-use std::alloc::{self, Layout};
-use std::borrow;
-use std::cmp::Ordering;
-use std::convert::From;
-use std::fmt;
-use std::hash::{Hash, Hasher};
-use std::iter::{ExactSizeIterator, Iterator};
-use std::marker::PhantomData;
-use std::mem::{self, align_of, size_of};
-use std::ops::{Deref, DerefMut};
-use std::os::raw::c_void;
-use std::process;
-use std::ptr;
-use std::slice;
-use std::sync::atomic;
-use std::sync::atomic::Ordering::{Acquire, Relaxed, Release};
-use std::{isize, usize};
-
-/// A soft limit on the amount of references that may be made to an `Arc`.
-///
-/// Going above this limit will abort your program (although not
-/// necessarily) at _exactly_ `MAX_REFCOUNT + 1` references.
-const MAX_REFCOUNT: usize = (isize::MAX) as usize;
-
-/// Special refcount value that means the data is not reference counted,
-/// and that the `Arc` is really acting as a read-only static reference.
-const STATIC_REFCOUNT: usize = usize::MAX;
-
-/// An atomically reference counted shared pointer
-///
-/// See the documentation for [`Arc`] in the standard library. Unlike the
-/// standard library `Arc`, this `Arc` does not support weak reference counting.
-///
-/// See the discussion in https://github.com/rust-lang/rust/pull/60594 for the
-/// usage of PhantomData.
-///
-/// [`Arc`]: https://doc.rust-lang.org/stable/std/sync/struct.Arc.html
-///
-/// cbindgen:derive-eq=false
-/// cbindgen:derive-neq=false
-#[repr(C)]
-pub struct Arc<T: ?Sized> {
- p: ptr::NonNull<ArcInner<T>>,
- phantom: PhantomData<T>,
-}
-
-/// An `Arc` that is known to be uniquely owned
-///
-/// When `Arc`s are constructed, they are known to be
-/// uniquely owned. In such a case it is safe to mutate
-/// the contents of the `Arc`. Normally, one would just handle
-/// this by mutating the data on the stack before allocating the
-/// `Arc`, however it's possible the data is large or unsized
-/// and you need to heap-allocate it earlier in such a way
-/// that it can be freely converted into a regular `Arc` once you're
-/// done.
-///
-/// `UniqueArc` exists for this purpose, when constructed it performs
-/// the same allocations necessary for an `Arc`, however it allows mutable access.
-/// Once the mutation is finished, you can call `.shareable()` and get a regular `Arc`
-/// out of it.
-///
-/// Ignore the doctest below there's no way to skip building with refcount
-/// logging during doc tests (see rust-lang/rust#45599).
-///
-/// ```rust,ignore
-/// # use servo_arc::UniqueArc;
-/// let data = [1, 2, 3, 4, 5];
-/// let mut x = UniqueArc::new(data);
-/// x[4] = 7; // mutate!
-/// let y = x.shareable(); // y is an Arc<T>
-/// ```
-pub struct UniqueArc<T: ?Sized>(Arc<T>);
-
-impl<T> UniqueArc<T> {
- #[inline]
- /// Construct a new UniqueArc
- pub fn new(data: T) -> Self {
- UniqueArc(Arc::new(data))
- }
-
- /// Construct an uninitialized arc
- #[inline]
- pub fn new_uninit() -> UniqueArc<mem::MaybeUninit<T>> {
- unsafe {
- let layout = Layout::new::<ArcInner<mem::MaybeUninit<T>>>();
- let ptr = alloc::alloc(layout);
- let mut p = ptr::NonNull::new(ptr)
- .unwrap_or_else(|| alloc::handle_alloc_error(layout))
- .cast::<ArcInner<mem::MaybeUninit<T>>>();
- ptr::write(&mut p.as_mut().count, atomic::AtomicUsize::new(1));
-
- #[cfg(feature = "gecko_refcount_logging")]
- {
- NS_LogCtor(p.as_ptr() as *mut _, b"ServoArc\0".as_ptr() as *const _, 8)
- }
-
- UniqueArc(Arc {
- p,
- phantom: PhantomData,
- })
- }
- }
-
- #[inline]
- /// Convert to a shareable Arc<T> once we're done mutating it
- pub fn shareable(self) -> Arc<T> {
- self.0
- }
-}
-
-impl<T> UniqueArc<mem::MaybeUninit<T>> {
- /// Convert to an initialized Arc.
- #[inline]
- pub unsafe fn assume_init(this: Self) -> UniqueArc<T> {
- UniqueArc(Arc {
- p: mem::ManuallyDrop::new(this).0.p.cast(),
- phantom: PhantomData,
- })
- }
-}
-
-impl<T> Deref for UniqueArc<T> {
- type Target = T;
- fn deref(&self) -> &T {
- &*self.0
- }
-}
-
-impl<T> DerefMut for UniqueArc<T> {
- fn deref_mut(&mut self) -> &mut T {
- // We know this to be uniquely owned
- unsafe { &mut (*self.0.ptr()).data }
- }
-}
-
-unsafe impl<T: ?Sized + Sync + Send> Send for Arc<T> {}
-unsafe impl<T: ?Sized + Sync + Send> Sync for Arc<T> {}
-
-/// The object allocated by an Arc<T>
-#[repr(C)]
-struct ArcInner<T: ?Sized> {
- count: atomic::AtomicUsize,
- data: T,
-}
-
-unsafe impl<T: ?Sized + Sync + Send> Send for ArcInner<T> {}
-unsafe impl<T: ?Sized + Sync + Send> Sync for ArcInner<T> {}
-
-/// Computes the offset of the data field within ArcInner.
-fn data_offset<T>() -> usize {
- let size = size_of::<ArcInner<()>>();
- let align = align_of::<T>();
- // https://github.com/rust-lang/rust/blob/1.36.0/src/libcore/alloc.rs#L187-L207
- size.wrapping_add(align).wrapping_sub(1) & !align.wrapping_sub(1)
-}
-
-impl<T> Arc<T> {
- /// Construct an `Arc<T>`
- #[inline]
- pub fn new(data: T) -> Self {
- let ptr = Box::into_raw(Box::new(ArcInner {
- count: atomic::AtomicUsize::new(1),
- data,
- }));
-
- #[cfg(feature = "gecko_refcount_logging")]
- unsafe {
- // FIXME(emilio): Would be so amazing to have
- // std::intrinsics::type_name() around, so that we could also report
- // a real size.
- NS_LogCtor(ptr as *mut _, b"ServoArc\0".as_ptr() as *const _, 8);
- }
-
- unsafe {
- Arc {
- p: ptr::NonNull::new_unchecked(ptr),
- phantom: PhantomData,
- }
- }
- }
-
- /// Construct an intentionally-leaked arc.
- #[inline]
- pub fn new_leaked(data: T) -> Self {
- let arc = Self::new(data);
- arc.mark_as_intentionally_leaked();
- arc
- }
-
- /// Convert the Arc<T> to a raw pointer, suitable for use across FFI
- ///
- /// Note: This returns a pointer to the data T, which is offset in the allocation.
- #[inline]
- pub fn into_raw(this: Self) -> *const T {
- let ptr = unsafe { &((*this.ptr()).data) as *const _ };
- mem::forget(this);
- ptr
- }
-
- /// Reconstruct the Arc<T> from a raw pointer obtained from into_raw()
- ///
- /// Note: This raw pointer will be offset in the allocation and must be preceded
- /// by the atomic count.
- #[inline]
- pub unsafe fn from_raw(ptr: *const T) -> Self {
- // To find the corresponding pointer to the `ArcInner` we need
- // to subtract the offset of the `data` field from the pointer.
- let ptr = (ptr as *const u8).sub(data_offset::<T>());
- Arc {
- p: ptr::NonNull::new_unchecked(ptr as *mut ArcInner<T>),
- phantom: PhantomData,
- }
- }
-
- /// Like from_raw, but returns an addrefed arc instead.
- #[inline]
- pub unsafe fn from_raw_addrefed(ptr: *const T) -> Self {
- let arc = Self::from_raw(ptr);
- mem::forget(arc.clone());
- arc
- }
-
- /// Create a new static Arc<T> (one that won't reference count the object)
- /// and place it in the allocation provided by the specified `alloc`
- /// function.
- ///
- /// `alloc` must return a pointer into a static allocation suitable for
- /// storing data with the `Layout` passed into it. The pointer returned by
- /// `alloc` will not be freed.
- #[inline]
- pub unsafe fn new_static<F>(alloc: F, data: T) -> Arc<T>
- where
- F: FnOnce(Layout) -> *mut u8,
- {
- let ptr = alloc(Layout::new::<ArcInner<T>>()) as *mut ArcInner<T>;
-
- let x = ArcInner {
- count: atomic::AtomicUsize::new(STATIC_REFCOUNT),
- data,
- };
-
- ptr::write(ptr, x);
-
- Arc {
- p: ptr::NonNull::new_unchecked(ptr),
- phantom: PhantomData,
- }
- }
-
- /// Produce a pointer to the data that can be converted back
- /// to an Arc. This is basically an `&Arc<T>`, without the extra indirection.
- /// It has the benefits of an `&T` but also knows about the underlying refcount
- /// and can be converted into more `Arc<T>`s if necessary.
- #[inline]
- pub fn borrow_arc<'a>(&'a self) -> ArcBorrow<'a, T> {
- ArcBorrow(&**self)
- }
-
- /// Returns the address on the heap of the Arc itself -- not the T within it -- for memory
- /// reporting.
- ///
- /// If this is a static reference, this returns null.
- pub fn heap_ptr(&self) -> *const c_void {
- if self.inner().count.load(Relaxed) == STATIC_REFCOUNT {
- ptr::null()
- } else {
- self.p.as_ptr() as *const ArcInner<T> as *const c_void
- }
- }
-}
-
-impl<T: ?Sized> Arc<T> {
- #[inline]
- fn inner(&self) -> &ArcInner<T> {
- // This unsafety is ok because while this arc is alive we're guaranteed
- // that the inner pointer is valid. Furthermore, we know that the
- // `ArcInner` structure itself is `Sync` because the inner data is
- // `Sync` as well, so we're ok loaning out an immutable pointer to these
- // contents.
- unsafe { &*self.ptr() }
- }
-
- #[inline(always)]
- fn record_drop(&self) {
- #[cfg(feature = "gecko_refcount_logging")]
- unsafe {
- NS_LogDtor(self.ptr() as *mut _, b"ServoArc\0".as_ptr() as *const _, 8);
- }
- }
-
- /// Marks this `Arc` as intentionally leaked for the purposes of refcount
- /// logging.
- ///
- /// It's a logic error to call this more than once, but it's not unsafe, as
- /// it'd just report negative leaks.
- #[inline(always)]
- pub fn mark_as_intentionally_leaked(&self) {
- self.record_drop();
- }
-
- // Non-inlined part of `drop`. Just invokes the destructor and calls the
- // refcount logging machinery if enabled.
- #[inline(never)]
- unsafe fn drop_slow(&mut self) {
- self.record_drop();
- let _ = Box::from_raw(self.ptr());
- }
-
- /// Test pointer equality between the two Arcs, i.e. they must be the _same_
- /// allocation
- #[inline]
- pub fn ptr_eq(this: &Self, other: &Self) -> bool {
- this.ptr() == other.ptr()
- }
-
- fn ptr(&self) -> *mut ArcInner<T> {
- self.p.as_ptr()
- }
-}
-
-#[cfg(feature = "gecko_refcount_logging")]
-extern "C" {
- fn NS_LogCtor(
- aPtr: *mut std::os::raw::c_void,
- aTypeName: *const std::os::raw::c_char,
- aSize: u32,
- );
- fn NS_LogDtor(
- aPtr: *mut std::os::raw::c_void,
- aTypeName: *const std::os::raw::c_char,
- aSize: u32,
- );
-}
-
-impl<T: ?Sized> Clone for Arc<T> {
- #[inline]
- fn clone(&self) -> Self {
- // NOTE(emilio): If you change anything here, make sure that the
- // implementation in layout/style/ServoStyleConstsInlines.h matches!
- //
- // Using a relaxed ordering to check for STATIC_REFCOUNT is safe, since
- // `count` never changes between STATIC_REFCOUNT and other values.
- if self.inner().count.load(Relaxed) != STATIC_REFCOUNT {
- // Using a relaxed ordering is alright here, as knowledge of the
- // original reference prevents other threads from erroneously deleting
- // the object.
- //
- // As explained in the [Boost documentation][1], Increasing the
- // reference counter can always be done with memory_order_relaxed: New
- // references to an object can only be formed from an existing
- // reference, and passing an existing reference from one thread to
- // another must already provide any required synchronization.
- //
- // [1]: (www.boost.org/doc/libs/1_55_0/doc/html/atomic/usage_examples.html)
- let old_size = self.inner().count.fetch_add(1, Relaxed);
-
- // However we need to guard against massive refcounts in case someone
- // is `mem::forget`ing Arcs. If we don't do this the count can overflow
- // and users will use-after free. We racily saturate to `isize::MAX` on
- // the assumption that there aren't ~2 billion threads incrementing
- // the reference count at once. This branch will never be taken in
- // any realistic program.
- //
- // We abort because such a program is incredibly degenerate, and we
- // don't care to support it.
- if old_size > MAX_REFCOUNT {
- process::abort();
- }
- }
-
- unsafe {
- Arc {
- p: ptr::NonNull::new_unchecked(self.ptr()),
- phantom: PhantomData,
- }
- }
- }
-}
-
-impl<T: ?Sized> Deref for Arc<T> {
- type Target = T;
-
- #[inline]
- fn deref(&self) -> &T {
- &self.inner().data
- }
-}
-
-impl<T: Clone> Arc<T> {
- /// Makes a mutable reference to the `Arc`, cloning if necessary
- ///
- /// This is functionally equivalent to [`Arc::make_mut`][mm] from the standard library.
- ///
- /// If this `Arc` is uniquely owned, `make_mut()` will provide a mutable
- /// reference to the contents. If not, `make_mut()` will create a _new_ `Arc`
- /// with a copy of the contents, update `this` to point to it, and provide
- /// a mutable reference to its contents.
- ///
- /// This is useful for implementing copy-on-write schemes where you wish to
- /// avoid copying things if your `Arc` is not shared.
- ///
- /// [mm]: https://doc.rust-lang.org/stable/std/sync/struct.Arc.html#method.make_mut
- #[inline]
- pub fn make_mut(this: &mut Self) -> &mut T {
- if !this.is_unique() {
- // Another pointer exists; clone
- *this = Arc::new((**this).clone());
- }
-
- unsafe {
- // This unsafety is ok because we're guaranteed that the pointer
- // returned is the *only* pointer that will ever be returned to T. Our
- // reference count is guaranteed to be 1 at this point, and we required
- // the Arc itself to be `mut`, so we're returning the only possible
- // reference to the inner data.
- &mut (*this.ptr()).data
- }
- }
-}
-
-impl<T: ?Sized> Arc<T> {
- /// Provides mutable access to the contents _if_ the `Arc` is uniquely owned.
- #[inline]
- pub fn get_mut(this: &mut Self) -> Option<&mut T> {
- if this.is_unique() {
- unsafe {
- // See make_mut() for documentation of the threadsafety here.
- Some(&mut (*this.ptr()).data)
- }
- } else {
- None
- }
- }
-
- /// Whether or not the `Arc` is a static reference.
- #[inline]
- pub fn is_static(&self) -> bool {
- // Using a relaxed ordering to check for STATIC_REFCOUNT is safe, since
- // `count` never changes between STATIC_REFCOUNT and other values.
- self.inner().count.load(Relaxed) == STATIC_REFCOUNT
- }
-
- /// Whether or not the `Arc` is uniquely owned (is the refcount 1?) and not
- /// a static reference.
- #[inline]
- pub fn is_unique(&self) -> bool {
- // See the extensive discussion in [1] for why this needs to be Acquire.
- //
- // [1] https://github.com/servo/servo/issues/21186
- self.inner().count.load(Acquire) == 1
- }
-}
-
-impl<T: ?Sized> Drop for Arc<T> {
- #[inline]
- fn drop(&mut self) {
- // NOTE(emilio): If you change anything here, make sure that the
- // implementation in layout/style/ServoStyleConstsInlines.h matches!
- if self.is_static() {
- return;
- }
-
- // Because `fetch_sub` is already atomic, we do not need to synchronize
- // with other threads unless we are going to delete the object.
- if self.inner().count.fetch_sub(1, Release) != 1 {
- return;
- }
-
- // FIXME(bholley): Use the updated comment when [2] is merged.
- //
- // This load is needed to prevent reordering of use of the data and
- // deletion of the data. Because it is marked `Release`, the decreasing
- // of the reference count synchronizes with this `Acquire` load. This
- // means that use of the data happens before decreasing the reference
- // count, which happens before this load, which happens before the
- // deletion of the data.
- //
- // As explained in the [Boost documentation][1],
- //
- // > It is important to enforce any possible access to the object in one
- // > thread (through an existing reference) to *happen before* deleting
- // > the object in a different thread. This is achieved by a "release"
- // > operation after dropping a reference (any access to the object
- // > through this reference must obviously happened before), and an
- // > "acquire" operation before deleting the object.
- //
- // [1]: (www.boost.org/doc/libs/1_55_0/doc/html/atomic/usage_examples.html)
- // [2]: https://github.com/rust-lang/rust/pull/41714
- self.inner().count.load(Acquire);
-
- unsafe {
- self.drop_slow();
- }
- }
-}
-
-impl<T: ?Sized + PartialEq> PartialEq for Arc<T> {
- fn eq(&self, other: &Arc<T>) -> bool {
- Self::ptr_eq(self, other) || *(*self) == *(*other)
- }
-
- fn ne(&self, other: &Arc<T>) -> bool {
- !Self::ptr_eq(self, other) && *(*self) != *(*other)
- }
-}
-
-impl<T: ?Sized + PartialOrd> PartialOrd for Arc<T> {
- fn partial_cmp(&self, other: &Arc<T>) -> Option<Ordering> {
- (**self).partial_cmp(&**other)
- }
-
- fn lt(&self, other: &Arc<T>) -> bool {
- *(*self) < *(*other)
- }
-
- fn le(&self, other: &Arc<T>) -> bool {
- *(*self) <= *(*other)
- }
-
- fn gt(&self, other: &Arc<T>) -> bool {
- *(*self) > *(*other)
- }
-
- fn ge(&self, other: &Arc<T>) -> bool {
- *(*self) >= *(*other)
- }
-}
-impl<T: ?Sized + Ord> Ord for Arc<T> {
- fn cmp(&self, other: &Arc<T>) -> Ordering {
- (**self).cmp(&**other)
- }
-}
-impl<T: ?Sized + Eq> Eq for Arc<T> {}
-
-impl<T: ?Sized + fmt::Display> fmt::Display for Arc<T> {
- fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
- fmt::Display::fmt(&**self, f)
- }
-}
-
-impl<T: ?Sized + fmt::Debug> fmt::Debug for Arc<T> {
- fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
- fmt::Debug::fmt(&**self, f)
- }
-}
-
-impl<T: ?Sized> fmt::Pointer for Arc<T> {
- fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
- fmt::Pointer::fmt(&self.ptr(), f)
- }
-}
-
-impl<T: Default> Default for Arc<T> {
- fn default() -> Arc<T> {
- Arc::new(Default::default())
- }
-}
-
-impl<T: ?Sized + Hash> Hash for Arc<T> {
- fn hash<H: Hasher>(&self, state: &mut H) {
- (**self).hash(state)
- }
-}
-
-impl<T> From<T> for Arc<T> {
- #[inline]
- fn from(t: T) -> Self {
- Arc::new(t)
- }
-}
-
-impl<T: ?Sized> borrow::Borrow<T> for Arc<T> {
- #[inline]
- fn borrow(&self) -> &T {
- &**self
- }
-}
-
-impl<T: ?Sized> AsRef<T> for Arc<T> {
- #[inline]
- fn as_ref(&self) -> &T {
- &**self
- }
-}
-
-unsafe impl<T: ?Sized> StableDeref for Arc<T> {}
-unsafe impl<T: ?Sized> CloneStableDeref for Arc<T> {}
-
-#[cfg(feature = "servo")]
-impl<'de, T: Deserialize<'de>> Deserialize<'de> for Arc<T> {
- fn deserialize<D>(deserializer: D) -> Result<Arc<T>, D::Error>
- where
- D: ::serde::de::Deserializer<'de>,
- {
- T::deserialize(deserializer).map(Arc::new)
- }
-}
-
-#[cfg(feature = "servo")]
-impl<T: Serialize> Serialize for Arc<T> {
- fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
- where
- S: ::serde::ser::Serializer,
- {
- (**self).serialize(serializer)
- }
-}
-
-/// Structure to allow Arc-managing some fixed-sized data and a variably-sized
-/// slice in a single allocation.
-#[derive(Debug, Eq, PartialEq, PartialOrd)]
-#[repr(C)]
-pub struct HeaderSlice<H, T: ?Sized> {
- /// The fixed-sized data.
- pub header: H,
-
- /// The dynamically-sized data.
- pub slice: T,
-}
-
-#[inline(always)]
-fn divide_rounding_up(dividend: usize, divisor: usize) -> usize {
- (dividend + divisor - 1) / divisor
-}
-
-impl<H, T> Arc<HeaderSlice<H, [T]>> {
- /// Creates an Arc for a HeaderSlice using the given header struct and
- /// iterator to generate the slice.
- ///
- /// `is_static` indicates whether to create a static Arc.
- ///
- /// `alloc` is used to get a pointer to the memory into which the
- /// dynamically sized ArcInner<HeaderSlice<H, T>> value will be
- /// written. If `is_static` is true, then `alloc` must return a
- /// pointer into some static memory allocation. If it is false,
- /// then `alloc` must return an allocation that can be dellocated
- /// by calling Box::from_raw::<ArcInner<HeaderSlice<H, T>>> on it.
- #[inline]
- fn from_header_and_iter_alloc<F, I>(
- alloc: F,
- header: H,
- mut items: I,
- num_items: usize,
- is_static: bool,
- ) -> Self
- where
- F: FnOnce(Layout) -> *mut u8,
- I: Iterator<Item = T>,
- {
- assert_ne!(size_of::<T>(), 0, "Need to think about ZST");
-
- let inner_align = align_of::<ArcInner<HeaderSlice<H, [T; 0]>>>();
- debug_assert!(inner_align >= align_of::<T>());
-
- // Compute the required size for the allocation.
- let size = {
- // Next, synthesize a totally garbage (but properly aligned) pointer
- // to a sequence of T.
- let fake_slice_ptr = inner_align as *const T;
-
- // Convert that sequence to a fat pointer. The address component of
- // the fat pointer will be garbage, but the length will be correct.
- let fake_slice = unsafe { slice::from_raw_parts(fake_slice_ptr, num_items) };
-
- // Pretend the garbage address points to our allocation target (with
- // a trailing sequence of T), rather than just a sequence of T.
- let fake_ptr = fake_slice as *const [T] as *const ArcInner<HeaderSlice<H, [T]>>;
- let fake_ref: &ArcInner<HeaderSlice<H, [T]>> = unsafe { &*fake_ptr };
-
- // Use size_of_val, which will combine static information about the
- // type with the length from the fat pointer. The garbage address
- // will not be used.
- mem::size_of_val(fake_ref)
- };
-
- let ptr: *mut ArcInner<HeaderSlice<H, [T]>>;
- unsafe {
- // Allocate the buffer.
- let layout = if inner_align <= align_of::<usize>() {
- Layout::from_size_align_unchecked(size, align_of::<usize>())
- } else if inner_align <= align_of::<u64>() {
- // On 32-bit platforms <T> may have 8 byte alignment while usize
- // has 4 byte aligment. Use u64 to avoid over-alignment.
- // This branch will compile away in optimized builds.
- Layout::from_size_align_unchecked(size, align_of::<u64>())
- } else {
- panic!("Over-aligned type not handled");
- };
-
- let buffer = alloc(layout);
-
- // Synthesize the fat pointer. We do this by claiming we have a direct
- // pointer to a [T], and then changing the type of the borrow. The key
- // point here is that the length portion of the fat pointer applies
- // only to the number of elements in the dynamically-sized portion of
- // the type, so the value will be the same whether it points to a [T]
- // or something else with a [T] as its last member.
- let fake_slice: &mut [T] = slice::from_raw_parts_mut(buffer as *mut T, num_items);
- ptr = fake_slice as *mut [T] as *mut ArcInner<HeaderSlice<H, [T]>>;
-
- // Write the data.
- //
- // Note that any panics here (i.e. from the iterator) are safe, since
- // we'll just leak the uninitialized memory.
- let count = if is_static {
- atomic::AtomicUsize::new(STATIC_REFCOUNT)
- } else {
- atomic::AtomicUsize::new(1)
- };
- ptr::write(&mut ((*ptr).count), count);
- ptr::write(&mut ((*ptr).data.header), header);
- if num_items != 0 {
- let mut current: *mut T = &mut (*ptr).data.slice[0];
- for _ in 0..num_items {
- ptr::write(
- current,
- items
- .next()
- .expect("ExactSizeIterator over-reported length"),
- );
- current = current.offset(1);
- }
- // We should have consumed the buffer exactly, maybe accounting
- // for some padding from the alignment.
- debug_assert!(
- (buffer.add(size) as usize - current as *mut u8 as usize) < inner_align
- );
- }
- assert!(
- items.next().is_none(),
- "ExactSizeIterator under-reported length"
- );
- }
- #[cfg(feature = "gecko_refcount_logging")]
- unsafe {
- if !is_static {
- // FIXME(emilio): Would be so amazing to have
- // std::intrinsics::type_name() around.
- NS_LogCtor(ptr as *mut _, b"ServoArc\0".as_ptr() as *const _, 8)
- }
- }
-
- // Return the fat Arc.
- assert_eq!(
- size_of::<Self>(),
- size_of::<usize>() * 2,
- "The Arc will be fat"
- );
- unsafe {
- Arc {
- p: ptr::NonNull::new_unchecked(ptr),
- phantom: PhantomData,
- }
- }
- }
-
- /// Creates an Arc for a HeaderSlice using the given header struct and iterator to generate the
- /// slice. Panics if num_items doesn't match the number of items.
- #[inline]
- pub fn from_header_and_iter_with_size<I>(header: H, items: I, num_items: usize) -> Self
- where
- I: Iterator<Item = T>,
- {
- Arc::from_header_and_iter_alloc(
- |layout| {
- // align will only ever be align_of::<usize>() or align_of::<u64>()
- let align = layout.align();
- unsafe {
- if align == mem::align_of::<usize>() {
- Self::allocate_buffer::<usize>(layout.size())
- } else {
- assert_eq!(align, mem::align_of::<u64>());
- Self::allocate_buffer::<u64>(layout.size())
- }
- }
- },
- header,
- items,
- num_items,
- /* is_static = */ false,
- )
- }
-
- /// Creates an Arc for a HeaderSlice using the given header struct and
- /// iterator to generate the slice. The resulting Arc will be fat.
- #[inline]
- pub fn from_header_and_iter<I>(header: H, items: I) -> Self
- where
- I: Iterator<Item = T> + ExactSizeIterator,
- {
- let len = items.len();
- Self::from_header_and_iter_with_size(header, items, len)
- }
-
- #[inline]
- unsafe fn allocate_buffer<W>(size: usize) -> *mut u8 {
- // We use Vec because the underlying allocation machinery isn't
- // available in stable Rust. To avoid alignment issues, we allocate
- // words rather than bytes, rounding up to the nearest word size.
- let words_to_allocate = divide_rounding_up(size, mem::size_of::<W>());
- let mut vec = Vec::<W>::with_capacity(words_to_allocate);
- vec.set_len(words_to_allocate);
- Box::into_raw(vec.into_boxed_slice()) as *mut W as *mut u8
- }
-}
-
-/// Header data with an inline length. Consumers that use HeaderWithLength as the
-/// Header type in HeaderSlice can take advantage of ThinArc.
-#[derive(Debug, Eq, PartialEq, PartialOrd)]
-#[repr(C)]
-pub struct HeaderWithLength<H> {
- /// The fixed-sized data.
- pub header: H,
-
- /// The slice length.
- length: usize,
-}
-
-impl<H> HeaderWithLength<H> {
- /// Creates a new HeaderWithLength.
- pub fn new(header: H, length: usize) -> Self {
- HeaderWithLength { header, length }
- }
-}
-
-type HeaderSliceWithLength<H, T> = HeaderSlice<HeaderWithLength<H>, T>;
-
-/// A "thin" `Arc` containing dynamically sized data
-///
-/// This is functionally equivalent to Arc<(H, [T])>
-///
-/// When you create an `Arc` containing a dynamically sized type
-/// like `HeaderSlice<H, [T]>`, the `Arc` is represented on the stack
-/// as a "fat pointer", where the length of the slice is stored
-/// alongside the `Arc`'s pointer. In some situations you may wish to
-/// have a thin pointer instead, perhaps for FFI compatibility
-/// or space efficiency.
-///
-/// Note that we use `[T; 0]` in order to have the right alignment for `T`.
-///
-/// `ThinArc` solves this by storing the length in the allocation itself,
-/// via `HeaderSliceWithLength`.
-#[repr(C)]
-pub struct ThinArc<H, T> {
- ptr: ptr::NonNull<ArcInner<HeaderSliceWithLength<H, [T; 0]>>>,
- phantom: PhantomData<(H, T)>,
-}
-
-impl<H: fmt::Debug, T: fmt::Debug> fmt::Debug for ThinArc<H, T> {
- fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
- fmt::Debug::fmt(self.deref(), f)
- }
-}
-
-unsafe impl<H: Sync + Send, T: Sync + Send> Send for ThinArc<H, T> {}
-unsafe impl<H: Sync + Send, T: Sync + Send> Sync for ThinArc<H, T> {}
-
-// Synthesize a fat pointer from a thin pointer.
-//
-// See the comment around the analogous operation in from_header_and_iter.
-fn thin_to_thick<H, T>(
- thin: *mut ArcInner<HeaderSliceWithLength<H, [T; 0]>>,
-) -> *mut ArcInner<HeaderSliceWithLength<H, [T]>> {
- let len = unsafe { (*thin).data.header.length };
- let fake_slice: *mut [T] = unsafe { slice::from_raw_parts_mut(thin as *mut T, len) };
-
- fake_slice as *mut ArcInner<HeaderSliceWithLength<H, [T]>>
-}
-
-impl<H, T> ThinArc<H, T> {
- /// Temporarily converts |self| into a bonafide Arc and exposes it to the
- /// provided callback. The refcount is not modified.
- #[inline]
- pub fn with_arc<F, U>(&self, f: F) -> U
- where
- F: FnOnce(&Arc<HeaderSliceWithLength<H, [T]>>) -> U,
- {
- // Synthesize transient Arc, which never touches the refcount of the ArcInner.
- let transient = unsafe {
- mem::ManuallyDrop::new(Arc {
- p: ptr::NonNull::new_unchecked(thin_to_thick(self.ptr.as_ptr())),
- phantom: PhantomData,
- })
- };
-
- // Expose the transient Arc to the callback, which may clone it if it wants.
- let result = f(&transient);
-
- // Forward the result.
- result
- }
-
- /// Creates a `ThinArc` for a HeaderSlice using the given header struct and
- /// iterator to generate the slice.
- pub fn from_header_and_iter<I>(header: H, items: I) -> Self
- where
- I: Iterator<Item = T> + ExactSizeIterator,
- {
- let header = HeaderWithLength::new(header, items.len());
- Arc::into_thin(Arc::from_header_and_iter(header, items))
- }
-
- /// Create a static `ThinArc` for a HeaderSlice using the given header
- /// struct and iterator to generate the slice, placing it in the allocation
- /// provided by the specified `alloc` function.
- ///
- /// `alloc` must return a pointer into a static allocation suitable for
- /// storing data with the `Layout` passed into it. The pointer returned by
- /// `alloc` will not be freed.
- pub unsafe fn static_from_header_and_iter<F, I>(alloc: F, header: H, items: I) -> Self
- where
- F: FnOnce(Layout) -> *mut u8,
- I: Iterator<Item = T> + ExactSizeIterator,
- {
- let len = items.len();
- let header = HeaderWithLength::new(header, len);
- Arc::into_thin(Arc::from_header_and_iter_alloc(
- alloc, header, items, len, /* is_static = */ true,
- ))
- }
-
- /// Returns the address on the heap of the ThinArc itself -- not the T
- /// within it -- for memory reporting, and bindings.
- #[inline]
- pub fn ptr(&self) -> *const c_void {
- self.ptr.as_ptr() as *const ArcInner<T> as *const c_void
- }
-
- /// If this is a static ThinArc, this returns null.
- #[inline]
- pub fn heap_ptr(&self) -> *const c_void {
- let is_static =
- ThinArc::with_arc(self, |a| a.inner().count.load(Relaxed) == STATIC_REFCOUNT);
- if is_static {
- ptr::null()
- } else {
- self.ptr()
- }
- }
-}
-
-impl<H, T> Deref for ThinArc<H, T> {
- type Target = HeaderSliceWithLength<H, [T]>;
-
- #[inline]
- fn deref(&self) -> &Self::Target {
- unsafe { &(*thin_to_thick(self.ptr.as_ptr())).data }
- }
-}
-
-impl<H, T> Clone for ThinArc<H, T> {
- #[inline]
- fn clone(&self) -> Self {
- ThinArc::with_arc(self, |a| Arc::into_thin(a.clone()))
- }
-}
-
-impl<H, T> Drop for ThinArc<H, T> {
- #[inline]
- fn drop(&mut self) {
- let _ = Arc::from_thin(ThinArc {
- ptr: self.ptr,
- phantom: PhantomData,
- });
- }
-}
-
-impl<H, T> Arc<HeaderSliceWithLength<H, [T]>> {
- /// Converts an `Arc` into a `ThinArc`. This consumes the `Arc`, so the refcount
- /// is not modified.
- #[inline]
- pub fn into_thin(a: Self) -> ThinArc<H, T> {
- assert_eq!(
- a.header.length,
- a.slice.len(),
- "Length needs to be correct for ThinArc to work"
- );
- let fat_ptr: *mut ArcInner<HeaderSliceWithLength<H, [T]>> = a.ptr();
- mem::forget(a);
- let thin_ptr = fat_ptr as *mut [usize] as *mut usize;
- ThinArc {
- ptr: unsafe {
- ptr::NonNull::new_unchecked(
- thin_ptr as *mut ArcInner<HeaderSliceWithLength<H, [T; 0]>>,
- )
- },
- phantom: PhantomData,
- }
- }
-
- /// Converts a `ThinArc` into an `Arc`. This consumes the `ThinArc`, so the refcount
- /// is not modified.
- #[inline]
- pub fn from_thin(a: ThinArc<H, T>) -> Self {
- let ptr = thin_to_thick(a.ptr.as_ptr());
- mem::forget(a);
- unsafe {
- Arc {
- p: ptr::NonNull::new_unchecked(ptr),
- phantom: PhantomData,
- }
- }
- }
-}
-
-impl<H, T> UniqueArc<HeaderSliceWithLength<H, [T]>> {
- #[inline]
- pub fn from_header_and_iter<I>(header: HeaderWithLength<H>, items: I) -> Self
- where
- I: Iterator<Item = T> + ExactSizeIterator,
- {
- Self(Arc::from_header_and_iter(header, items))
- }
-
- #[inline]
- pub fn from_header_and_iter_with_size<I>(
- header: HeaderWithLength<H>,
- items: I,
- num_items: usize,
- ) -> Self
- where
- I: Iterator<Item = T>,
- {
- Self(Arc::from_header_and_iter_with_size(
- header, items, num_items,
- ))
- }
-
- /// Returns a mutable reference to the header.
- pub fn header_mut(&mut self) -> &mut H {
- // We know this to be uniquely owned
- unsafe { &mut (*self.0.ptr()).data.header.header }
- }
-
- /// Returns a mutable reference to the slice.
- pub fn data_mut(&mut self) -> &mut [T] {
- // We know this to be uniquely owned
- unsafe { &mut (*self.0.ptr()).data.slice }
- }
-
- pub fn shareable_thin(self) -> ThinArc<H, T> {
- Arc::into_thin(self.0)
- }
-}
-
-impl<H: PartialEq, T: PartialEq> PartialEq for ThinArc<H, T> {
- #[inline]
- fn eq(&self, other: &ThinArc<H, T>) -> bool {
- ThinArc::with_arc(self, |a| ThinArc::with_arc(other, |b| *a == *b))
- }
-}
-
-impl<H: Eq, T: Eq> Eq for ThinArc<H, T> {}
-
-/// A "borrowed `Arc`". This is a pointer to
-/// a T that is known to have been allocated within an
-/// `Arc`.
-///
-/// This is equivalent in guarantees to `&Arc<T>`, however it is
-/// a bit more flexible. To obtain an `&Arc<T>` you must have
-/// an `Arc<T>` instance somewhere pinned down until we're done with it.
-/// It's also a direct pointer to `T`, so using this involves less pointer-chasing
-///
-/// However, C++ code may hand us refcounted things as pointers to T directly,
-/// so we have to conjure up a temporary `Arc` on the stack each time.
-///
-/// `ArcBorrow` lets us deal with borrows of known-refcounted objects
-/// without needing to worry about where the `Arc<T>` is.
-#[derive(Debug, Eq, PartialEq)]
-pub struct ArcBorrow<'a, T: 'a>(&'a T);
-
-impl<'a, T> Copy for ArcBorrow<'a, T> {}
-impl<'a, T> Clone for ArcBorrow<'a, T> {
- #[inline]
- fn clone(&self) -> Self {
- *self
- }
-}
-
-impl<'a, T> ArcBorrow<'a, T> {
- /// Clone this as an `Arc<T>`. This bumps the refcount.
- #[inline]
- pub fn clone_arc(&self) -> Arc<T> {
- let arc = unsafe { Arc::from_raw(self.0) };
- // addref it!
- mem::forget(arc.clone());
- arc
- }
-
- /// For constructing from a reference known to be Arc-backed,
- /// e.g. if we obtain such a reference over FFI
- #[inline]
- pub unsafe fn from_ref(r: &'a T) -> Self {
- ArcBorrow(r)
- }
-
- /// Compare two `ArcBorrow`s via pointer equality. Will only return
- /// true if they come from the same allocation
- pub fn ptr_eq(this: &Self, other: &Self) -> bool {
- this.0 as *const T == other.0 as *const T
- }
-
- /// Temporarily converts |self| into a bonafide Arc and exposes it to the
- /// provided callback. The refcount is not modified.
- #[inline]
- pub fn with_arc<F, U>(&self, f: F) -> U
- where
- F: FnOnce(&Arc<T>) -> U,
- T: 'static,
- {
- // Synthesize transient Arc, which never touches the refcount.
- let transient = unsafe { mem::ManuallyDrop::new(Arc::from_raw(self.0)) };
-
- // Expose the transient Arc to the callback, which may clone it if it wants.
- let result = f(&transient);
-
- // Forward the result.
- result
- }
-
- /// Similar to deref, but uses the lifetime |a| rather than the lifetime of
- /// self, which is incompatible with the signature of the Deref trait.
- #[inline]
- pub fn get(&self) -> &'a T {
- self.0
- }
-}
-
-impl<'a, T> Deref for ArcBorrow<'a, T> {
- type Target = T;
-
- #[inline]
- fn deref(&self) -> &T {
- self.0
- }
-}
-
-/// A tagged union that can represent `Arc<A>` or `Arc<B>` while only consuming a
-/// single word. The type is also `NonNull`, and thus can be stored in an Option
-/// without increasing size.
-///
-/// This is functionally equivalent to
-/// `enum ArcUnion<A, B> { First(Arc<A>), Second(Arc<B>)` but only takes up
-/// up a single word of stack space.
-///
-/// This could probably be extended to support four types if necessary.
-pub struct ArcUnion<A, B> {
- p: ptr::NonNull<()>,
- phantom_a: PhantomData<A>,
- phantom_b: PhantomData<B>,
-}
-
-unsafe impl<A: Sync + Send, B: Send + Sync> Send for ArcUnion<A, B> {}
-unsafe impl<A: Sync + Send, B: Send + Sync> Sync for ArcUnion<A, B> {}
-
-impl<A: PartialEq, B: PartialEq> PartialEq for ArcUnion<A, B> {
- fn eq(&self, other: &Self) -> bool {
- use crate::ArcUnionBorrow::*;
- match (self.borrow(), other.borrow()) {
- (First(x), First(y)) => x == y,
- (Second(x), Second(y)) => x == y,
- (_, _) => false,
- }
- }
-}
-
-/// This represents a borrow of an `ArcUnion`.
-#[derive(Debug)]
-pub enum ArcUnionBorrow<'a, A: 'a, B: 'a> {
- First(ArcBorrow<'a, A>),
- Second(ArcBorrow<'a, B>),
-}
-
-impl<A, B> ArcUnion<A, B> {
- unsafe fn new(ptr: *mut ()) -> Self {
- ArcUnion {
- p: ptr::NonNull::new_unchecked(ptr),
- phantom_a: PhantomData,
- phantom_b: PhantomData,
- }
- }
-
- /// Returns true if the two values are pointer-equal.
- #[inline]
- pub fn ptr_eq(this: &Self, other: &Self) -> bool {
- this.p == other.p
- }
-
- #[inline]
- pub fn ptr(&self) -> ptr::NonNull<()> {
- self.p
- }
-
- /// Returns an enum representing a borrow of either A or B.
- #[inline]
- pub fn borrow(&self) -> ArcUnionBorrow<A, B> {
- if self.is_first() {
- let ptr = self.p.as_ptr() as *const A;
- let borrow = unsafe { ArcBorrow::from_ref(&*ptr) };
- ArcUnionBorrow::First(borrow)
- } else {
- let ptr = ((self.p.as_ptr() as usize) & !0x1) as *const B;
- let borrow = unsafe { ArcBorrow::from_ref(&*ptr) };
- ArcUnionBorrow::Second(borrow)
- }
- }
-
- /// Creates an `ArcUnion` from an instance of the first type.
- pub fn from_first(other: Arc<A>) -> Self {
- unsafe { Self::new(Arc::into_raw(other) as *mut _) }
- }
-
- /// Creates an `ArcUnion` from an instance of the second type.
- pub fn from_second(other: Arc<B>) -> Self {
- unsafe { Self::new(((Arc::into_raw(other) as usize) | 0x1) as *mut _) }
- }
-
- /// Returns true if this `ArcUnion` contains the first type.
- pub fn is_first(&self) -> bool {
- self.p.as_ptr() as usize & 0x1 == 0
- }
-
- /// Returns true if this `ArcUnion` contains the second type.
- pub fn is_second(&self) -> bool {
- !self.is_first()
- }
-
- /// Returns a borrow of the first type if applicable, otherwise `None`.
- pub fn as_first(&self) -> Option<ArcBorrow<A>> {
- match self.borrow() {
- ArcUnionBorrow::First(x) => Some(x),
- ArcUnionBorrow::Second(_) => None,
- }
- }
-
- /// Returns a borrow of the second type if applicable, otherwise None.
- pub fn as_second(&self) -> Option<ArcBorrow<B>> {
- match self.borrow() {
- ArcUnionBorrow::First(_) => None,
- ArcUnionBorrow::Second(x) => Some(x),
- }
- }
-}
-
-impl<A, B> Clone for ArcUnion<A, B> {
- fn clone(&self) -> Self {
- match self.borrow() {
- ArcUnionBorrow::First(x) => ArcUnion::from_first(x.clone_arc()),
- ArcUnionBorrow::Second(x) => ArcUnion::from_second(x.clone_arc()),
- }
- }
-}
-
-impl<A, B> Drop for ArcUnion<A, B> {
- fn drop(&mut self) {
- match self.borrow() {
- ArcUnionBorrow::First(x) => unsafe {
- let _ = Arc::from_raw(&*x);
- },
- ArcUnionBorrow::Second(x) => unsafe {
- let _ = Arc::from_raw(&*x);
- },
- }
- }
-}
-
-impl<A: fmt::Debug, B: fmt::Debug> fmt::Debug for ArcUnion<A, B> {
- fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
- fmt::Debug::fmt(&self.borrow(), f)
- }
-}
-
-#[cfg(test)]
-mod tests {
- use super::{Arc, HeaderWithLength, ThinArc};
- use std::clone::Clone;
- use std::ops::Drop;
- use std::sync::atomic;
- use std::sync::atomic::Ordering::{Acquire, SeqCst};
-
- #[derive(PartialEq)]
- struct Canary(*mut atomic::AtomicUsize);
-
- impl Drop for Canary {
- fn drop(&mut self) {
- unsafe {
- (*self.0).fetch_add(1, SeqCst);
- }
- }
- }
-
- #[test]
- fn empty_thin() {
- let header = HeaderWithLength::new(100u32, 0);
- let x = Arc::from_header_and_iter(header, std::iter::empty::<i32>());
- let y = Arc::into_thin(x.clone());
- assert_eq!(y.header.header, 100);
- assert!(y.slice.is_empty());
- assert_eq!(x.header.header, 100);
- assert!(x.slice.is_empty());
- }
-
- #[test]
- fn thin_assert_padding() {
- #[derive(Clone, Default)]
- #[repr(C)]
- struct Padded {
- i: u16,
- }
-
- // The header will have more alignment than `Padded`
- let header = HeaderWithLength::new(0i32, 2);
- let items = vec![Padded { i: 0xdead }, Padded { i: 0xbeef }];
- let a = ThinArc::from_header_and_iter(header, items.into_iter());
- assert_eq!(a.slice.len(), 2);
- assert_eq!(a.slice[0].i, 0xdead);
- assert_eq!(a.slice[1].i, 0xbeef);
- }
-
- #[test]
- fn slices_and_thin() {
- let mut canary = atomic::AtomicUsize::new(0);
- let c = Canary(&mut canary as *mut atomic::AtomicUsize);
- let v = vec![5, 6];
- let header = HeaderWithLength::new(c, v.len());
- {
- let x = Arc::into_thin(Arc::from_header_and_iter(header, v.into_iter()));
- let y = ThinArc::with_arc(&x, |q| q.clone());
- let _ = y.clone();
- let _ = x == x;
- Arc::from_thin(x.clone());
- }
- assert_eq!(canary.load(Acquire), 1);
- }
-}
diff --git a/components/servo_arc/rustfmt.toml b/components/servo_arc/rustfmt.toml
deleted file mode 100644
index c7ad93bafe3..00000000000
--- a/components/servo_arc/rustfmt.toml
+++ /dev/null
@@ -1 +0,0 @@
-disable_all_formatting = true
diff --git a/components/shared/canvas/Cargo.toml b/components/shared/canvas/Cargo.toml
index 6f3eca8c160..6fd52f4e3a4 100644
--- a/components/shared/canvas/Cargo.toml
+++ b/components/shared/canvas/Cargo.toml
@@ -20,14 +20,14 @@ cssparser = { workspace = true }
euclid = { workspace = true }
ipc-channel = { workspace = true }
lazy_static = { workspace = true }
-malloc_size_of = { path = "../../malloc_size_of" }
+malloc_size_of = { workspace = true }
malloc_size_of_derive = { workspace = true }
pixels = { path = "../../pixels" }
serde = { workspace = true }
serde_bytes = { workspace = true }
servo_config = { path = "../../config" }
sparkle = { workspace = true }
-style = { path = "../../style" }
+style = { workspace = true }
time = { workspace = true, optional = true }
webrender_api = { workspace = true }
webxr-api = { git = "https://github.com/servo/webxr", features = ["ipc"] }
diff --git a/components/shared/devtools/Cargo.toml b/components/shared/devtools/Cargo.toml
index d158a3117ab..c3be3bcb01b 100644
--- a/components/shared/devtools/Cargo.toml
+++ b/components/shared/devtools/Cargo.toml
@@ -14,7 +14,7 @@ path = "lib.rs"
bitflags = { workspace = true }
http = { workspace = true }
ipc-channel = { workspace = true }
-malloc_size_of = { path = "../../malloc_size_of" }
+malloc_size_of = { workspace = true }
malloc_size_of_derive = { workspace = true }
msg = { workspace = true }
serde = { workspace = true }
diff --git a/components/shared/gfx/Cargo.toml b/components/shared/gfx/Cargo.toml
index f94b5870c35..23bf75bfb07 100644
--- a/components/shared/gfx/Cargo.toml
+++ b/components/shared/gfx/Cargo.toml
@@ -11,7 +11,7 @@ name = "gfx_traits"
path = "lib.rs"
[dependencies]
-malloc_size_of = { path = "../../malloc_size_of" }
+malloc_size_of = { workspace = true }
malloc_size_of_derive = { workspace = true }
range = { path = "../../range" }
serde = { workspace = true }
diff --git a/components/shared/msg/Cargo.toml b/components/shared/msg/Cargo.toml
index d64880b5c6f..97abd671eef 100644
--- a/components/shared/msg/Cargo.toml
+++ b/components/shared/msg/Cargo.toml
@@ -15,9 +15,9 @@ doctest = false
[dependencies]
ipc-channel = { workspace = true }
lazy_static = { workspace = true }
-malloc_size_of = { path = "../../malloc_size_of" }
+malloc_size_of = { workspace = true }
malloc_size_of_derive = { workspace = true }
parking_lot = { workspace = true }
serde = { workspace = true }
-size_of_test = { path = "../../size_of_test" }
+size_of_test = { workspace = true }
webrender_api = { workspace = true }
diff --git a/components/shared/net/Cargo.toml b/components/shared/net/Cargo.toml
index dffc41eeb3a..50e9d4eb21d 100644
--- a/components/shared/net/Cargo.toml
+++ b/components/shared/net/Cargo.toml
@@ -24,7 +24,7 @@ image = { workspace = true }
ipc-channel = { workspace = true }
lazy_static = { workspace = true }
log = { workspace = true }
-malloc_size_of = { path = "../../malloc_size_of" }
+malloc_size_of = { workspace = true }
malloc_size_of_derive = { workspace = true }
mime = { workspace = true }
msg = { workspace = true }
@@ -33,7 +33,7 @@ percent-encoding = { workspace = true }
pixels = { path = "../../pixels" }
rustls = { workspace = true }
serde = { workspace = true }
-servo_arc = { path = "../../servo_arc" }
+servo_arc = { workspace = true }
servo_rand = { path = "../../rand" }
servo_url = { path = "../../url" }
url = { workspace = true }
diff --git a/components/shared/script/Cargo.toml b/components/shared/script/Cargo.toml
index 64ea7be7e66..5cc2b1f8554 100644
--- a/components/shared/script/Cargo.toml
+++ b/components/shared/script/Cargo.toml
@@ -26,7 +26,7 @@ ipc-channel = { workspace = true }
keyboard-types = { workspace = true }
libc = { workspace = true }
log = { workspace = true }
-malloc_size_of = { path = "../../malloc_size_of" }
+malloc_size_of = { workspace = true }
malloc_size_of_derive = { workspace = true }
media = { path = "../../media" }
msg = { workspace = true }
@@ -34,7 +34,7 @@ net_traits = { workspace = true }
pixels = { path = "../../pixels" }
profile_traits = { workspace = true }
serde = { workspace = true }
-servo_atoms = { path = "../../atoms" }
+servo_atoms = { workspace = true }
servo_url = { path = "../../url" }
smallvec = { workspace = true }
style_traits = { workspace = true }
diff --git a/components/shared/script_layout/Cargo.toml b/components/shared/script_layout/Cargo.toml
index 40c92e96c8e..df526c9bec9 100644
--- a/components/shared/script_layout/Cargo.toml
+++ b/components/shared/script_layout/Cargo.toml
@@ -21,7 +21,7 @@ gfx_traits = { workspace = true }
html5ever = { workspace = true }
ipc-channel = { workspace = true }
libc = { workspace = true }
-malloc_size_of = { path = "../../malloc_size_of" }
+malloc_size_of = { workspace = true }
malloc_size_of_derive = { workspace = true }
metrics = { path = "../../metrics" }
msg = { workspace = true }
@@ -29,10 +29,10 @@ net_traits = { workspace = true }
profile_traits = { workspace = true }
range = { path = "../../range" }
script_traits = { workspace = true }
-selectors = { path = "../../selectors" }
-servo_arc = { path = "../../servo_arc" }
-servo_atoms = { path = "../../atoms" }
+selectors = { workspace = true }
+servo_arc = { workspace = true }
+servo_atoms = { workspace = true }
servo_url = { path = "../../url" }
-style = { path = "../../style", features = ["servo"] }
+style = { workspace = true }
style_traits = { workspace = true }
webrender_api = { workspace = true }
diff --git a/components/size_of_test/Cargo.toml b/components/size_of_test/Cargo.toml
deleted file mode 100644
index faea55c5c1c..00000000000
--- a/components/size_of_test/Cargo.toml
+++ /dev/null
@@ -1,13 +0,0 @@
-[package]
-name = "size_of_test"
-version = "0.0.1"
-authors = ["The Servo Project Developers"]
-license = "MPL-2.0"
-edition = "2018"
-publish = false
-
-[lib]
-path = "lib.rs"
-
-[dependencies]
-static_assertions = "1.1"
diff --git a/components/size_of_test/lib.rs b/components/size_of_test/lib.rs
deleted file mode 100644
index 18e45175e8c..00000000000
--- a/components/size_of_test/lib.rs
+++ /dev/null
@@ -1,14 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
-
-pub use static_assertions::const_assert_eq;
-
-/// Asserts the size of a type at compile time.
-#[macro_export]
-macro_rules! size_of_test {
- ($t: ty, $expected_size: expr) => {
- #[cfg(target_pointer_width = "64")]
- $crate::const_assert_eq!(std::mem::size_of::<$t>(), $expected_size);
- };
-}
diff --git a/components/style/Cargo.toml b/components/style/Cargo.toml
deleted file mode 100644
index 6040b62116b..00000000000
--- a/components/style/Cargo.toml
+++ /dev/null
@@ -1,95 +0,0 @@
-[package]
-name = "style"
-version = "0.0.1"
-authors = ["The Servo Project Developers"]
-license = "MPL-2.0"
-publish = false
-
-build = "build.rs"
-edition = "2018"
-
-# https://github.com/rust-lang/cargo/issues/3544
-links = "servo_style_crate"
-
-[lib]
-name = "style"
-path = "lib.rs"
-doctest = false
-
-[features]
-gecko = ["style_traits/gecko", "bindgen", "regex", "toml", "mozbuild"]
-servo = [
- "serde",
- "style_traits/servo",
- "servo_atoms",
- "html5ever",
- "cssparser/serde",
- "encoding_rs",
- "malloc_size_of/servo",
- "string_cache",
- "to_shmem/servo",
- "servo_arc/servo",
- "url",
-]
-gecko_debug = []
-gecko_refcount_logging = []
-
-[dependencies]
-app_units = "0.7"
-arrayvec = "0.7"
-atomic_refcell = "0.1"
-bitflags = "1.0"
-byteorder = "1.0"
-cssparser = { workspace = true }
-derive_more = { version = "0.99", default-features = false, features = ["add", "add_assign", "deref", "from"] }
-encoding_rs = { version = "0.8", optional = true }
-euclid = "0.22"
-fxhash = "0.2"
-html5ever = { version = "0.26", optional = true }
-indexmap = "1.0"
-itertools = "0.10"
-itoa = "1.0"
-lazy_static = "1"
-log = { version = "0.4", features = ["std"] }
-malloc_size_of = { path = "../malloc_size_of" }
-malloc_size_of_derive = "0.1"
-mime = "0.3.13"
-new_debug_unreachable = "1.0"
-num-derive = "0.3"
-num-integer = "0.1"
-num-traits = "0.2"
-num_cpus = { version = "1.1.0" }
-owning_ref = "0.4"
-parking_lot = "0.12"
-precomputed-hash = "0.1.1"
-rayon = "1"
-selectors = { path = "../selectors" }
-serde = { version = "1.0", optional = true, features = ["derive"] }
-servo_arc = { path = "../servo_arc" }
-servo_atoms = { path = "../atoms", optional = true }
-smallbitvec = "2.3.0"
-smallvec = "1.0"
-static_assertions = "1.1"
-static_prefs = { path = "../style_static_prefs" }
-string_cache = { version = "0.8", optional = true }
-style_config = { path = "../style_config" }
-style_derive = { path = "../style_derive" }
-style_traits = { path = "../style_traits" }
-time = "0.1"
-thin-vec = { workspace = true }
-to_shmem = { path = "../to_shmem" }
-to_shmem_derive = { path = "../to_shmem_derive" }
-uluru = "3.0"
-unicode-bidi = "0.3"
-unicode-segmentation = "1.0"
-url = { workspace = true, optional = true }
-void = "1.0.2"
-
-[build-dependencies]
-bindgen = { version = "0.69", optional = true, default-features = false }
-lazy_static = "1"
-log = "0.4"
-mozbuild = { version = "0.1", optional = true }
-regex = { version = "1.1", optional = true }
-toml = { version = "0.5", optional = true, default-features = false }
-walkdir = "2.1.4"
diff --git a/components/style/README.md b/components/style/README.md
deleted file mode 100644
index bdbe36e44c4..00000000000
--- a/components/style/README.md
+++ /dev/null
@@ -1,6 +0,0 @@
-servo-style
-===========
-
-Style system for Servo, using [rust-cssparser](https://github.com/servo/rust-cssparser) for parsing.
-
- * [Documentation](https://github.com/servo/servo/blob/main/docs/components/style.md).
diff --git a/components/style/animation.rs b/components/style/animation.rs
deleted file mode 100644
index aafe7067dc9..00000000000
--- a/components/style/animation.rs
+++ /dev/null
@@ -1,1415 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
-
-//! CSS transitions and animations.
-
-// NOTE(emilio): This code isn't really executed in Gecko, but we don't want to
-// compile it out so that people remember it exists.
-
-use crate::context::{CascadeInputs, SharedStyleContext};
-use crate::dom::{OpaqueNode, TDocument, TElement, TNode};
-use crate::properties::animated_properties::{AnimationValue, AnimationValueMap};
-use crate::properties::longhands::animation_direction::computed_value::single_value::T as AnimationDirection;
-use crate::properties::longhands::animation_fill_mode::computed_value::single_value::T as AnimationFillMode;
-use crate::properties::longhands::animation_play_state::computed_value::single_value::T as AnimationPlayState;
-use crate::properties::AnimationDeclarations;
-use crate::properties::{
- ComputedValues, Importance, LonghandId, LonghandIdSet, PropertyDeclarationBlock,
- PropertyDeclarationId,
-};
-use crate::rule_tree::CascadeLevel;
-use crate::selector_parser::PseudoElement;
-use crate::shared_lock::{Locked, SharedRwLock};
-use crate::style_resolver::StyleResolverForElement;
-use crate::stylesheets::keyframes_rule::{KeyframesAnimation, KeyframesStep, KeyframesStepValue};
-use crate::stylesheets::layer_rule::LayerOrder;
-use crate::values::animated::{Animate, Procedure};
-use crate::values::computed::{Time, TimingFunction};
-use crate::values::generics::easing::BeforeFlag;
-use crate::Atom;
-use fxhash::FxHashMap;
-use parking_lot::RwLock;
-use servo_arc::Arc;
-use std::fmt;
-
-/// Represents an animation for a given property.
-#[derive(Clone, Debug, MallocSizeOf)]
-pub struct PropertyAnimation {
- /// The value we are animating from.
- from: AnimationValue,
-
- /// The value we are animating to.
- to: AnimationValue,
-
- /// The timing function of this `PropertyAnimation`.
- timing_function: TimingFunction,
-
- /// The duration of this `PropertyAnimation` in seconds.
- pub duration: f64,
-}
-
-impl PropertyAnimation {
- /// Returns the given property longhand id.
- pub fn property_id(&self) -> LonghandId {
- debug_assert_eq!(self.from.id(), self.to.id());
- self.from.id()
- }
-
- fn from_longhand(
- longhand: LonghandId,
- timing_function: TimingFunction,
- duration: Time,
- old_style: &ComputedValues,
- new_style: &ComputedValues,
- ) -> Option<PropertyAnimation> {
- // FIXME(emilio): Handle the case where old_style and new_style's writing mode differ.
- let longhand = longhand.to_physical(new_style.writing_mode);
- let from = AnimationValue::from_computed_values(longhand, old_style)?;
- let to = AnimationValue::from_computed_values(longhand, new_style)?;
- let duration = duration.seconds() as f64;
-
- if from == to || duration == 0.0 {
- return None;
- }
-
- Some(PropertyAnimation {
- from,
- to,
- timing_function,
- duration,
- })
- }
-
- /// The output of the timing function given the progress ration of this animation.
- fn timing_function_output(&self, progress: f64) -> f64 {
- let epsilon = 1. / (200. * self.duration);
- // FIXME: Need to set the before flag correctly.
- // In order to get the before flag, we have to know the current animation phase
- // and whether the iteration is reversed. For now, we skip this calculation
- // by treating as if the flag is unset at all times.
- // https://drafts.csswg.org/css-easing/#step-timing-function-algo
- self.timing_function
- .calculate_output(progress, BeforeFlag::Unset, epsilon)
- }
-
- /// Update the given animation at a given point of progress.
- fn calculate_value(&self, progress: f64) -> Result<AnimationValue, ()> {
- let procedure = Procedure::Interpolate {
- progress: self.timing_function_output(progress),
- };
- self.from.animate(&self.to, procedure)
- }
-}
-
-/// This structure represents the state of an animation.
-#[derive(Clone, Debug, MallocSizeOf, PartialEq)]
-pub enum AnimationState {
- /// The animation has been created, but is not running yet. This state
- /// is also used when an animation is still in the first delay phase.
- Pending,
- /// This animation is currently running.
- Running,
- /// This animation is paused. The inner field is the percentage of progress
- /// when it was paused, from 0 to 1.
- Paused(f64),
- /// This animation has finished.
- Finished,
- /// This animation has been canceled.
- Canceled,
-}
-
-impl AnimationState {
- /// Whether or not this state requires its owning animation to be ticked.
- fn needs_to_be_ticked(&self) -> bool {
- *self == AnimationState::Running || *self == AnimationState::Pending
- }
-}
-
-/// This structure represents a keyframes animation current iteration state.
-///
-/// If the iteration count is infinite, there's no other state, otherwise we
-/// have to keep track the current iteration and the max iteration count.
-#[derive(Clone, Debug, MallocSizeOf)]
-pub enum KeyframesIterationState {
- /// Infinite iterations with the current iteration count.
- Infinite(f64),
- /// Current and max iterations.
- Finite(f64, f64),
-}
-
-/// A temporary data structure used when calculating ComputedKeyframes for an
-/// animation. This data structure is used to collapse information for steps
-/// which may be spread across multiple keyframe declarations into a single
-/// instance per `start_percentage`.
-struct IntermediateComputedKeyframe {
- declarations: PropertyDeclarationBlock,
- timing_function: Option<TimingFunction>,
- start_percentage: f32,
-}
-
-impl IntermediateComputedKeyframe {
- fn new(start_percentage: f32) -> Self {
- IntermediateComputedKeyframe {
- declarations: PropertyDeclarationBlock::new(),
- timing_function: None,
- start_percentage,
- }
- }
-
- /// Walk through all keyframe declarations and combine all declarations with the
- /// same `start_percentage` into individual `IntermediateComputedKeyframe`s.
- fn generate_for_keyframes(
- animation: &KeyframesAnimation,
- context: &SharedStyleContext,
- base_style: &ComputedValues,
- ) -> Vec<Self> {
- let mut intermediate_steps: Vec<Self> = Vec::with_capacity(animation.steps.len());
- let mut current_step = IntermediateComputedKeyframe::new(0.);
- for step in animation.steps.iter() {
- let start_percentage = step.start_percentage.0;
- if start_percentage != current_step.start_percentage {
- let new_step = IntermediateComputedKeyframe::new(start_percentage);
- intermediate_steps.push(std::mem::replace(&mut current_step, new_step));
- }
-
- current_step.update_from_step(step, context, base_style);
- }
- intermediate_steps.push(current_step);
-
- // We should always have a first and a last step, even if these are just
- // generated by KeyframesStepValue::ComputedValues.
- debug_assert!(intermediate_steps.first().unwrap().start_percentage == 0.);
- debug_assert!(intermediate_steps.last().unwrap().start_percentage == 1.);
-
- intermediate_steps
- }
-
- fn update_from_step(
- &mut self,
- step: &KeyframesStep,
- context: &SharedStyleContext,
- base_style: &ComputedValues,
- ) {
- // Each keyframe declaration may optionally specify a timing function, falling
- // back to the one defined global for the animation.
- let guard = &context.guards.author;
- if let Some(timing_function) = step.get_animation_timing_function(&guard) {
- self.timing_function = Some(timing_function.to_computed_value_without_context());
- }
-
- let block = match step.value {
- KeyframesStepValue::ComputedValues => return,
- KeyframesStepValue::Declarations { ref block } => block,
- };
-
- // Filter out !important, non-animatable properties, and the
- // 'display' property (which is only animatable from SMIL).
- let guard = block.read_with(&guard);
- for declaration in guard.normal_declaration_iter() {
- if let PropertyDeclarationId::Longhand(id) = declaration.id() {
- if id == LonghandId::Display {
- continue;
- }
-
- if !id.is_animatable() {
- continue;
- }
- }
-
- self.declarations.push(
- declaration.to_physical(base_style.writing_mode),
- Importance::Normal,
- );
- }
- }
-
- fn resolve_style<E>(
- self,
- element: E,
- context: &SharedStyleContext,
- base_style: &Arc<ComputedValues>,
- resolver: &mut StyleResolverForElement<E>,
- ) -> Arc<ComputedValues>
- where
- E: TElement,
- {
- if !self.declarations.any_normal() {
- return base_style.clone();
- }
-
- let document = element.as_node().owner_doc();
- let locked_block = Arc::new(document.shared_lock().wrap(self.declarations));
- let mut important_rules_changed = false;
- let rule_node = base_style.rules().clone();
- let new_node = context.stylist.rule_tree().update_rule_at_level(
- CascadeLevel::Animations,
- LayerOrder::root(),
- Some(locked_block.borrow_arc()),
- &rule_node,
- &context.guards,
- &mut important_rules_changed,
- );
-
- if new_node.is_none() {
- return base_style.clone();
- }
-
- let inputs = CascadeInputs {
- rules: new_node,
- visited_rules: base_style.visited_rules().cloned(),
- flags: base_style.flags.for_cascade_inputs(),
- };
- resolver
- .cascade_style_and_visited_with_default_parents(inputs)
- .0
- }
-}
-
-/// A single computed keyframe for a CSS Animation.
-#[derive(Clone, MallocSizeOf)]
-struct ComputedKeyframe {
- /// The timing function to use for transitions between this step
- /// and the next one.
- timing_function: TimingFunction,
-
- /// The starting percentage (a number between 0 and 1) which represents
- /// at what point in an animation iteration this step is.
- start_percentage: f32,
-
- /// The animation values to transition to and from when processing this
- /// keyframe animation step.
- values: Vec<AnimationValue>,
-}
-
-impl ComputedKeyframe {
- fn generate_for_keyframes<E>(
- element: E,
- animation: &KeyframesAnimation,
- context: &SharedStyleContext,
- base_style: &Arc<ComputedValues>,
- default_timing_function: TimingFunction,
- resolver: &mut StyleResolverForElement<E>,
- ) -> Vec<Self>
- where
- E: TElement,
- {
- let mut animating_properties = LonghandIdSet::new();
- for property in animation.properties_changed.iter() {
- debug_assert!(property.is_animatable());
- animating_properties.insert(property.to_physical(base_style.writing_mode));
- }
-
- let animation_values_from_style: Vec<AnimationValue> = animating_properties
- .iter()
- .map(|property| {
- AnimationValue::from_computed_values(property, &**base_style)
- .expect("Unexpected non-animatable property.")
- })
- .collect();
-
- let intermediate_steps =
- IntermediateComputedKeyframe::generate_for_keyframes(animation, context, base_style);
-
- let mut computed_steps: Vec<Self> = Vec::with_capacity(intermediate_steps.len());
- for (step_index, step) in intermediate_steps.into_iter().enumerate() {
- let start_percentage = step.start_percentage;
- let properties_changed_in_step = step.declarations.longhands().clone();
- let step_timing_function = step.timing_function.clone();
- let step_style = step.resolve_style(element, context, base_style, resolver);
- let timing_function =
- step_timing_function.unwrap_or_else(|| default_timing_function.clone());
-
- let values = {
- // If a value is not set in a property declaration we use the value from
- // the style for the first and last keyframe. For intermediate ones, we
- // use the value from the previous keyframe.
- //
- // TODO(mrobinson): According to the spec, we should use an interpolated
- // value for properties missing from keyframe declarations.
- let default_values = if start_percentage == 0. || start_percentage == 1.0 {
- &animation_values_from_style
- } else {
- debug_assert!(step_index != 0);
- &computed_steps[step_index - 1].values
- };
-
- // For each property that is animating, pull the value from the resolved
- // style for this step if it's in one of the declarations. Otherwise, we
- // use the default value from the set we calculated above.
- animating_properties
- .iter()
- .zip(default_values.iter())
- .map(|(longhand, default_value)| {
- if properties_changed_in_step.contains(longhand) {
- AnimationValue::from_computed_values(longhand, &step_style)
- .unwrap_or_else(|| default_value.clone())
- } else {
- default_value.clone()
- }
- })
- .collect()
- };
-
- computed_steps.push(ComputedKeyframe {
- timing_function,
- start_percentage,
- values,
- });
- }
- computed_steps
- }
-}
-
-/// A CSS Animation
-#[derive(Clone, MallocSizeOf)]
-pub struct Animation {
- /// The name of this animation as defined by the style.
- pub name: Atom,
-
- /// The properties that change in this animation.
- properties_changed: LonghandIdSet,
-
- /// The computed style for each keyframe of this animation.
- computed_steps: Vec<ComputedKeyframe>,
-
- /// The time this animation started at, which is the current value of the animation
- /// timeline when this animation was created plus any animation delay.
- pub started_at: f64,
-
- /// The duration of this animation.
- pub duration: f64,
-
- /// The delay of the animation.
- pub delay: f64,
-
- /// The `animation-fill-mode` property of this animation.
- pub fill_mode: AnimationFillMode,
-
- /// The current iteration state for the animation.
- pub iteration_state: KeyframesIterationState,
-
- /// Whether this animation is paused.
- pub state: AnimationState,
-
- /// The declared animation direction of this animation.
- pub direction: AnimationDirection,
-
- /// The current animation direction. This can only be `normal` or `reverse`.
- pub current_direction: AnimationDirection,
-
- /// The original cascade style, needed to compute the generated keyframes of
- /// the animation.
- #[ignore_malloc_size_of = "ComputedValues"]
- pub cascade_style: Arc<ComputedValues>,
-
- /// Whether or not this animation is new and or has already been tracked
- /// by the script thread.
- pub is_new: bool,
-}
-
-impl Animation {
- /// Whether or not this animation is cancelled by changes from a new style.
- fn is_cancelled_in_new_style(&self, new_style: &Arc<ComputedValues>) -> bool {
- let new_ui = new_style.get_ui();
- let index = new_ui
- .animation_name_iter()
- .position(|animation_name| Some(&self.name) == animation_name.as_atom());
- let index = match index {
- Some(index) => index,
- None => return true,
- };
-
- new_ui.animation_duration_mod(index).seconds() == 0.
- }
-
- /// Given the current time, advances this animation to the next iteration,
- /// updates times, and then toggles the direction if appropriate. Otherwise
- /// does nothing. Returns true if this animation has iterated.
- pub fn iterate_if_necessary(&mut self, time: f64) -> bool {
- if !self.iteration_over(time) {
- return false;
- }
-
- // Only iterate animations that are currently running.
- if self.state != AnimationState::Running {
- return false;
- }
-
- if self.on_last_iteration() {
- return false;
- }
-
- self.iterate();
- true
- }
-
- fn iterate(&mut self) {
- debug_assert!(!self.on_last_iteration());
-
- if let KeyframesIterationState::Finite(ref mut current, max) = self.iteration_state {
- *current = (*current + 1.).min(max);
- }
-
- if let AnimationState::Paused(ref mut progress) = self.state {
- debug_assert!(*progress > 1.);
- *progress -= 1.;
- }
-
- // Update the next iteration direction if applicable.
- self.started_at += self.duration;
- match self.direction {
- AnimationDirection::Alternate | AnimationDirection::AlternateReverse => {
- self.current_direction = match self.current_direction {
- AnimationDirection::Normal => AnimationDirection::Reverse,
- AnimationDirection::Reverse => AnimationDirection::Normal,
- _ => unreachable!(),
- };
- },
- _ => {},
- }
- }
-
- /// A number (> 0 and <= 1) which represents the fraction of a full iteration
- /// that the current iteration of the animation lasts. This will be less than 1
- /// if the current iteration is the fractional remainder of a non-integral
- /// iteration count.
- pub fn current_iteration_end_progress(&self) -> f64 {
- match self.iteration_state {
- KeyframesIterationState::Finite(current, max) => (max - current).min(1.),
- KeyframesIterationState::Infinite(_) => 1.,
- }
- }
-
- /// The duration of the current iteration of this animation which may be less
- /// than the animation duration if it has a non-integral iteration count.
- pub fn current_iteration_duration(&self) -> f64 {
- self.current_iteration_end_progress() * self.duration
- }
-
- /// Whether or not the current iteration is over. Note that this method assumes that
- /// the animation is still running.
- fn iteration_over(&self, time: f64) -> bool {
- time > (self.started_at + self.current_iteration_duration())
- }
-
- /// Assuming this animation is running, whether or not it is on the last iteration.
- fn on_last_iteration(&self) -> bool {
- match self.iteration_state {
- KeyframesIterationState::Finite(current, max) => current >= (max - 1.),
- KeyframesIterationState::Infinite(_) => false,
- }
- }
-
- /// Whether or not this animation has finished at the provided time. This does
- /// not take into account canceling i.e. when an animation or transition is
- /// canceled due to changes in the style.
- pub fn has_ended(&self, time: f64) -> bool {
- if !self.on_last_iteration() {
- return false;
- }
-
- let progress = match self.state {
- AnimationState::Finished => return true,
- AnimationState::Paused(progress) => progress,
- AnimationState::Running => (time - self.started_at) / self.duration,
- AnimationState::Pending | AnimationState::Canceled => return false,
- };
-
- progress >= self.current_iteration_end_progress()
- }
-
- /// Updates the appropiate state from other animation.
- ///
- /// This happens when an animation is re-submitted to layout, presumably
- /// because of an state change.
- ///
- /// There are some bits of state we can't just replace, over all taking in
- /// account times, so here's that logic.
- pub fn update_from_other(&mut self, other: &Self, now: f64) {
- use self::AnimationState::*;
-
- debug!(
- "KeyframesAnimationState::update_from_other({:?}, {:?})",
- self, other
- );
-
- // NB: We shall not touch the started_at field, since we don't want to
- // restart the animation.
- let old_started_at = self.started_at;
- let old_duration = self.duration;
- let old_direction = self.current_direction;
- let old_state = self.state.clone();
- let old_iteration_state = self.iteration_state.clone();
-
- *self = other.clone();
-
- self.started_at = old_started_at;
- self.current_direction = old_direction;
-
- // Don't update the iteration count, just the iteration limit.
- // TODO: see how changing the limit affects rendering in other browsers.
- // We might need to keep the iteration count even when it's infinite.
- match (&mut self.iteration_state, old_iteration_state) {
- (
- &mut KeyframesIterationState::Finite(ref mut iters, _),
- KeyframesIterationState::Finite(old_iters, _),
- ) => *iters = old_iters,
- _ => {},
- }
-
- // Don't pause or restart animations that should remain finished.
- // We call mem::replace because `has_ended(...)` looks at `Animation::state`.
- let new_state = std::mem::replace(&mut self.state, Running);
- if old_state == Finished && self.has_ended(now) {
- self.state = Finished;
- } else {
- self.state = new_state;
- }
-
- // If we're unpausing the animation, fake the start time so we seem to
- // restore it.
- //
- // If the animation keeps paused, keep the old value.
- //
- // If we're pausing the animation, compute the progress value.
- match (&mut self.state, &old_state) {
- (&mut Pending, &Paused(progress)) => {
- self.started_at = now - (self.duration * progress);
- },
- (&mut Paused(ref mut new), &Paused(old)) => *new = old,
- (&mut Paused(ref mut progress), &Running) => {
- *progress = (now - old_started_at) / old_duration
- },
- _ => {},
- }
-
- // Try to detect when we should skip straight to the running phase to
- // avoid sending multiple animationstart events.
- if self.state == Pending && self.started_at <= now && old_state != Pending {
- self.state = Running;
- }
- }
-
- /// Fill in an `AnimationValueMap` with values calculated from this animation at
- /// the given time value.
- fn get_property_declaration_at_time(&self, now: f64, map: &mut AnimationValueMap) {
- debug_assert!(!self.computed_steps.is_empty());
-
- let total_progress = match self.state {
- AnimationState::Running | AnimationState::Pending | AnimationState::Finished => {
- (now - self.started_at) / self.duration
- },
- AnimationState::Paused(progress) => progress,
- AnimationState::Canceled => return,
- };
-
- if total_progress < 0. &&
- self.fill_mode != AnimationFillMode::Backwards &&
- self.fill_mode != AnimationFillMode::Both
- {
- return;
- }
- if self.has_ended(now) &&
- self.fill_mode != AnimationFillMode::Forwards &&
- self.fill_mode != AnimationFillMode::Both
- {
- return;
- }
- let total_progress = total_progress
- .min(self.current_iteration_end_progress())
- .max(0.0);
-
- // Get the indices of the previous (from) keyframe and the next (to) keyframe.
- let next_keyframe_index;
- let prev_keyframe_index;
- let num_steps = self.computed_steps.len();
- match self.current_direction {
- AnimationDirection::Normal => {
- next_keyframe_index = self
- .computed_steps
- .iter()
- .position(|step| total_progress as f32 <= step.start_percentage);
- prev_keyframe_index = next_keyframe_index
- .and_then(|pos| if pos != 0 { Some(pos - 1) } else { None })
- .unwrap_or(0);
- },
- AnimationDirection::Reverse => {
- next_keyframe_index = self
- .computed_steps
- .iter()
- .rev()
- .position(|step| total_progress as f32 <= 1. - step.start_percentage)
- .map(|pos| num_steps - pos - 1);
- prev_keyframe_index = next_keyframe_index
- .and_then(|pos| {
- if pos != num_steps - 1 {
- Some(pos + 1)
- } else {
- None
- }
- })
- .unwrap_or(num_steps - 1)
- },
- _ => unreachable!(),
- }
-
- debug!(
- "Animation::get_property_declaration_at_time: keyframe from {:?} to {:?}",
- prev_keyframe_index, next_keyframe_index
- );
-
- let prev_keyframe = &self.computed_steps[prev_keyframe_index];
- let next_keyframe = match next_keyframe_index {
- Some(index) => &self.computed_steps[index],
- None => return,
- };
-
- // If we only need to take into account one keyframe, then exit early
- // in order to avoid doing more work.
- let mut add_declarations_to_map = |keyframe: &ComputedKeyframe| {
- for value in keyframe.values.iter() {
- map.insert(value.id(), value.clone());
- }
- };
- if total_progress <= 0.0 {
- add_declarations_to_map(&prev_keyframe);
- return;
- }
- if total_progress >= 1.0 {
- add_declarations_to_map(&next_keyframe);
- return;
- }
-
- let percentage_between_keyframes =
- (next_keyframe.start_percentage - prev_keyframe.start_percentage).abs() as f64;
- let duration_between_keyframes = percentage_between_keyframes * self.duration;
- let direction_aware_prev_keyframe_start_percentage = match self.current_direction {
- AnimationDirection::Normal => prev_keyframe.start_percentage as f64,
- AnimationDirection::Reverse => 1. - prev_keyframe.start_percentage as f64,
- _ => unreachable!(),
- };
- let progress_between_keyframes = (total_progress -
- direction_aware_prev_keyframe_start_percentage) /
- percentage_between_keyframes;
-
- for (from, to) in prev_keyframe.values.iter().zip(next_keyframe.values.iter()) {
- let animation = PropertyAnimation {
- from: from.clone(),
- to: to.clone(),
- timing_function: prev_keyframe.timing_function.clone(),
- duration: duration_between_keyframes as f64,
- };
-
- if let Ok(value) = animation.calculate_value(progress_between_keyframes) {
- map.insert(value.id(), value);
- }
- }
- }
-}
-
-impl fmt::Debug for Animation {
- fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
- f.debug_struct("Animation")
- .field("name", &self.name)
- .field("started_at", &self.started_at)
- .field("duration", &self.duration)
- .field("delay", &self.delay)
- .field("iteration_state", &self.iteration_state)
- .field("state", &self.state)
- .field("direction", &self.direction)
- .field("current_direction", &self.current_direction)
- .field("cascade_style", &())
- .finish()
- }
-}
-
-/// A CSS Transition
-#[derive(Clone, Debug, MallocSizeOf)]
-pub struct Transition {
- /// The start time of this transition, which is the current value of the animation
- /// timeline when this transition was created plus any animation delay.
- pub start_time: f64,
-
- /// The delay used for this transition.
- pub delay: f64,
-
- /// The internal style `PropertyAnimation` for this transition.
- pub property_animation: PropertyAnimation,
-
- /// The state of this transition.
- pub state: AnimationState,
-
- /// Whether or not this transition is new and or has already been tracked
- /// by the script thread.
- pub is_new: bool,
-
- /// If this `Transition` has been replaced by a new one this field is
- /// used to help produce better reversed transitions.
- pub reversing_adjusted_start_value: AnimationValue,
-
- /// If this `Transition` has been replaced by a new one this field is
- /// used to help produce better reversed transitions.
- pub reversing_shortening_factor: f64,
-}
-
-impl Transition {
- fn update_for_possibly_reversed_transition(
- &mut self,
- replaced_transition: &Transition,
- delay: f64,
- now: f64,
- ) {
- // If we reach here, we need to calculate a reversed transition according to
- // https://drafts.csswg.org/css-transitions/#starting
- //
- // "...if the reversing-adjusted start value of the running transition
- // is the same as the value of the property in the after-change style (see
- // the section on reversing of transitions for why these case exists),
- // implementations must cancel the running transition and start
- // a new transition..."
- if replaced_transition.reversing_adjusted_start_value != self.property_animation.to {
- return;
- }
-
- // "* reversing-adjusted start value is the end value of the running transition"
- let replaced_animation = &replaced_transition.property_animation;
- self.reversing_adjusted_start_value = replaced_animation.to.clone();
-
- // "* reversing shortening factor is the absolute value, clamped to the
- // range [0, 1], of the sum of:
- // 1. the output of the timing function of the old transition at the
- // time of the style change event, times the reversing shortening
- // factor of the old transition
- // 2. 1 minus the reversing shortening factor of the old transition."
- let transition_progress = ((now - replaced_transition.start_time) /
- (replaced_transition.property_animation.duration))
- .min(1.0)
- .max(0.0);
- let timing_function_output = replaced_animation.timing_function_output(transition_progress);
- let old_reversing_shortening_factor = replaced_transition.reversing_shortening_factor;
- self.reversing_shortening_factor = ((timing_function_output *
- old_reversing_shortening_factor) +
- (1.0 - old_reversing_shortening_factor))
- .abs()
- .min(1.0)
- .max(0.0);
-
- // "* start time is the time of the style change event plus:
- // 1. if the matching transition delay is nonnegative, the matching
- // transition delay, or.
- // 2. if the matching transition delay is negative, the product of the new
- // transition’s reversing shortening factor and the matching transition delay,"
- self.start_time = if delay >= 0. {
- now + delay
- } else {
- now + (self.reversing_shortening_factor * delay)
- };
-
- // "* end time is the start time plus the product of the matching transition
- // duration and the new transition’s reversing shortening factor,"
- self.property_animation.duration *= self.reversing_shortening_factor;
-
- // "* start value is the current value of the property in the running transition,
- // * end value is the value of the property in the after-change style,"
- let procedure = Procedure::Interpolate {
- progress: timing_function_output,
- };
- match replaced_animation
- .from
- .animate(&replaced_animation.to, procedure)
- {
- Ok(new_start) => self.property_animation.from = new_start,
- Err(..) => {},
- }
- }
-
- /// Whether or not this animation has ended at the provided time. This does
- /// not take into account canceling i.e. when an animation or transition is
- /// canceled due to changes in the style.
- pub fn has_ended(&self, time: f64) -> bool {
- time >= self.start_time + (self.property_animation.duration)
- }
-
- /// Update the given animation at a given point of progress.
- pub fn calculate_value(&self, time: f64) -> Option<AnimationValue> {
- let progress = (time - self.start_time) / (self.property_animation.duration);
- if progress < 0.0 {
- return None;
- }
-
- self.property_animation
- .calculate_value(progress.min(1.0))
- .ok()
- }
-}
-
-/// Holds the animation state for a particular element.
-#[derive(Debug, Default, MallocSizeOf)]
-pub struct ElementAnimationSet {
- /// The animations for this element.
- pub animations: Vec<Animation>,
-
- /// The transitions for this element.
- pub transitions: Vec<Transition>,
-
- /// Whether or not this ElementAnimationSet has had animations or transitions
- /// which have been added, removed, or had their state changed.
- pub dirty: bool,
-}
-
-impl ElementAnimationSet {
- /// Cancel all animations in this `ElementAnimationSet`. This is typically called
- /// when the element has been removed from the DOM.
- pub fn cancel_all_animations(&mut self) {
- self.dirty = !self.animations.is_empty();
- for animation in self.animations.iter_mut() {
- animation.state = AnimationState::Canceled;
- }
- self.cancel_active_transitions();
- }
-
- fn cancel_active_transitions(&mut self) {
- for transition in self.transitions.iter_mut() {
- if transition.state != AnimationState::Finished {
- self.dirty = true;
- transition.state = AnimationState::Canceled;
- }
- }
- }
-
- /// Apply all active animations.
- pub fn apply_active_animations(
- &self,
- context: &SharedStyleContext,
- style: &mut Arc<ComputedValues>,
- ) {
- let now = context.current_time_for_animations;
- let mutable_style = Arc::make_mut(style);
- if let Some(map) = self.get_value_map_for_active_animations(now) {
- for value in map.values() {
- value.set_in_style_for_servo(mutable_style);
- }
- }
-
- if let Some(map) = self.get_value_map_for_active_transitions(now) {
- for value in map.values() {
- value.set_in_style_for_servo(mutable_style);
- }
- }
- }
-
- /// Clear all canceled animations and transitions from this `ElementAnimationSet`.
- pub fn clear_canceled_animations(&mut self) {
- self.animations
- .retain(|animation| animation.state != AnimationState::Canceled);
- self.transitions
- .retain(|animation| animation.state != AnimationState::Canceled);
- }
-
- /// Whether this `ElementAnimationSet` is empty, which means it doesn't
- /// hold any animations in any state.
- pub fn is_empty(&self) -> bool {
- self.animations.is_empty() && self.transitions.is_empty()
- }
-
- /// Whether or not this state needs animation ticks for its transitions
- /// or animations.
- pub fn needs_animation_ticks(&self) -> bool {
- self.animations
- .iter()
- .any(|animation| animation.state.needs_to_be_ticked()) ||
- self.transitions
- .iter()
- .any(|transition| transition.state.needs_to_be_ticked())
- }
-
- /// The number of running animations and transitions for this `ElementAnimationSet`.
- pub fn running_animation_and_transition_count(&self) -> usize {
- self.animations
- .iter()
- .filter(|animation| animation.state.needs_to_be_ticked())
- .count() +
- self.transitions
- .iter()
- .filter(|transition| transition.state.needs_to_be_ticked())
- .count()
- }
-
- /// If this `ElementAnimationSet` has any any active animations.
- pub fn has_active_animation(&self) -> bool {
- self.animations
- .iter()
- .any(|animation| animation.state != AnimationState::Canceled)
- }
-
- /// If this `ElementAnimationSet` has any any active transitions.
- pub fn has_active_transition(&self) -> bool {
- self.transitions
- .iter()
- .any(|transition| transition.state != AnimationState::Canceled)
- }
-
- /// Update our animations given a new style, canceling or starting new animations
- /// when appropriate.
- pub fn update_animations_for_new_style<E>(
- &mut self,
- element: E,
- context: &SharedStyleContext,
- new_style: &Arc<ComputedValues>,
- resolver: &mut StyleResolverForElement<E>,
- ) where
- E: TElement,
- {
- for animation in self.animations.iter_mut() {
- if animation.is_cancelled_in_new_style(new_style) {
- animation.state = AnimationState::Canceled;
- }
- }
-
- maybe_start_animations(element, &context, &new_style, self, resolver);
- }
-
- /// Update our transitions given a new style, canceling or starting new animations
- /// when appropriate.
- pub fn update_transitions_for_new_style(
- &mut self,
- might_need_transitions_update: bool,
- context: &SharedStyleContext,
- old_style: Option<&Arc<ComputedValues>>,
- after_change_style: &Arc<ComputedValues>,
- ) {
- // If this is the first style, we don't trigger any transitions and we assume
- // there were no previously triggered transitions.
- let mut before_change_style = match old_style {
- Some(old_style) => Arc::clone(old_style),
- None => return,
- };
-
- // If the style of this element is display:none, then cancel all active transitions.
- if after_change_style.get_box().clone_display().is_none() {
- self.cancel_active_transitions();
- return;
- }
-
- if !might_need_transitions_update {
- return;
- }
-
- // We convert old values into `before-change-style` here.
- if self.has_active_transition() || self.has_active_animation() {
- self.apply_active_animations(context, &mut before_change_style);
- }
-
- let transitioning_properties = start_transitions_if_applicable(
- context,
- &before_change_style,
- after_change_style,
- self,
- );
-
- // Cancel any non-finished transitions that have properties which no longer transition.
- for transition in self.transitions.iter_mut() {
- if transition.state == AnimationState::Finished {
- continue;
- }
- if transitioning_properties.contains(transition.property_animation.property_id()) {
- continue;
- }
- transition.state = AnimationState::Canceled;
- self.dirty = true;
- }
- }
-
- fn start_transition_if_applicable(
- &mut self,
- context: &SharedStyleContext,
- longhand_id: LonghandId,
- index: usize,
- old_style: &ComputedValues,
- new_style: &Arc<ComputedValues>,
- ) {
- if !longhand_id.is_transitionable() {
- return;
- }
-
- let style = new_style.get_ui();
- let timing_function = style.transition_timing_function_mod(index);
- let duration = style.transition_duration_mod(index);
- let delay = style.transition_delay_mod(index).seconds() as f64;
- let now = context.current_time_for_animations;
-
- // Only start a new transition if the style actually changes between
- // the old style and the new style.
- let property_animation = match PropertyAnimation::from_longhand(
- longhand_id,
- timing_function,
- duration,
- old_style,
- new_style,
- ) {
- Some(property_animation) => property_animation,
- None => return,
- };
-
- // Per [1], don't trigger a new transition if the end state for that
- // transition is the same as that of a transition that's running or
- // completed. We don't take into account any canceled animations.
- // [1]: https://drafts.csswg.org/css-transitions/#starting
- if self
- .transitions
- .iter()
- .filter(|transition| transition.state != AnimationState::Canceled)
- .any(|transition| transition.property_animation.to == property_animation.to)
- {
- return;
- }
-
- // We are going to start a new transition, but we might have to update
- // it if we are replacing a reversed transition.
- let reversing_adjusted_start_value = property_animation.from.clone();
- let mut new_transition = Transition {
- start_time: now + delay,
- delay,
- property_animation,
- state: AnimationState::Pending,
- is_new: true,
- reversing_adjusted_start_value,
- reversing_shortening_factor: 1.0,
- };
-
- if let Some(old_transition) = self
- .transitions
- .iter_mut()
- .filter(|transition| transition.state == AnimationState::Running)
- .find(|transition| transition.property_animation.property_id() == longhand_id)
- {
- // We always cancel any running transitions for the same property.
- old_transition.state = AnimationState::Canceled;
- new_transition.update_for_possibly_reversed_transition(old_transition, delay, now);
- }
-
- self.transitions.push(new_transition);
- self.dirty = true;
- }
-
- /// Generate a `AnimationValueMap` for this `ElementAnimationSet`'s
- /// active transitions at the given time value.
- pub fn get_value_map_for_active_transitions(&self, now: f64) -> Option<AnimationValueMap> {
- if !self.has_active_transition() {
- return None;
- }
-
- let mut map =
- AnimationValueMap::with_capacity_and_hasher(self.transitions.len(), Default::default());
- for transition in &self.transitions {
- if transition.state == AnimationState::Canceled {
- continue;
- }
- let value = match transition.calculate_value(now) {
- Some(value) => value,
- None => continue,
- };
- map.insert(value.id(), value);
- }
-
- Some(map)
- }
-
- /// Generate a `AnimationValueMap` for this `ElementAnimationSet`'s
- /// active animations at the given time value.
- pub fn get_value_map_for_active_animations(&self, now: f64) -> Option<AnimationValueMap> {
- if !self.has_active_animation() {
- return None;
- }
-
- let mut map = Default::default();
- for animation in &self.animations {
- animation.get_property_declaration_at_time(now, &mut map);
- }
-
- Some(map)
- }
-}
-
-#[derive(Clone, Debug, Eq, Hash, MallocSizeOf, PartialEq)]
-/// A key that is used to identify nodes in the `DocumentAnimationSet`.
-pub struct AnimationSetKey {
- /// The node for this `AnimationSetKey`.
- pub node: OpaqueNode,
- /// The pseudo element for this `AnimationSetKey`. If `None` this key will
- /// refer to the main content for its node.
- pub pseudo_element: Option<PseudoElement>,
-}
-
-impl AnimationSetKey {
- /// Create a new key given a node and optional pseudo element.
- pub fn new(node: OpaqueNode, pseudo_element: Option<PseudoElement>) -> Self {
- AnimationSetKey {
- node,
- pseudo_element,
- }
- }
-
- /// Create a new key for the main content of this node.
- pub fn new_for_non_pseudo(node: OpaqueNode) -> Self {
- AnimationSetKey {
- node,
- pseudo_element: None,
- }
- }
-
- /// Create a new key for given node and pseudo element.
- pub fn new_for_pseudo(node: OpaqueNode, pseudo_element: PseudoElement) -> Self {
- AnimationSetKey {
- node,
- pseudo_element: Some(pseudo_element),
- }
- }
-}
-
-#[derive(Clone, Debug, Default, MallocSizeOf)]
-/// A set of animations for a document.
-pub struct DocumentAnimationSet {
- /// The `ElementAnimationSet`s that this set contains.
- #[ignore_malloc_size_of = "Arc is hard"]
- pub sets: Arc<RwLock<FxHashMap<AnimationSetKey, ElementAnimationSet>>>,
-}
-
-impl DocumentAnimationSet {
- /// Return whether or not the provided node has active CSS animations.
- pub fn has_active_animations(&self, key: &AnimationSetKey) -> bool {
- self.sets
- .read()
- .get(key)
- .map_or(false, |set| set.has_active_animation())
- }
-
- /// Return whether or not the provided node has active CSS transitions.
- pub fn has_active_transitions(&self, key: &AnimationSetKey) -> bool {
- self.sets
- .read()
- .get(key)
- .map_or(false, |set| set.has_active_transition())
- }
-
- /// Return a locked PropertyDeclarationBlock with animation values for the given
- /// key and time.
- pub fn get_animation_declarations(
- &self,
- key: &AnimationSetKey,
- time: f64,
- shared_lock: &SharedRwLock,
- ) -> Option<Arc<Locked<PropertyDeclarationBlock>>> {
- self.sets
- .read()
- .get(key)
- .and_then(|set| set.get_value_map_for_active_animations(time))
- .map(|map| {
- let block = PropertyDeclarationBlock::from_animation_value_map(&map);
- Arc::new(shared_lock.wrap(block))
- })
- }
-
- /// Return a locked PropertyDeclarationBlock with transition values for the given
- /// key and time.
- pub fn get_transition_declarations(
- &self,
- key: &AnimationSetKey,
- time: f64,
- shared_lock: &SharedRwLock,
- ) -> Option<Arc<Locked<PropertyDeclarationBlock>>> {
- self.sets
- .read()
- .get(key)
- .and_then(|set| set.get_value_map_for_active_transitions(time))
- .map(|map| {
- let block = PropertyDeclarationBlock::from_animation_value_map(&map);
- Arc::new(shared_lock.wrap(block))
- })
- }
-
- /// Get all the animation declarations for the given key, returning an empty
- /// `AnimationDeclarations` if there are no animations.
- pub fn get_all_declarations(
- &self,
- key: &AnimationSetKey,
- time: f64,
- shared_lock: &SharedRwLock,
- ) -> AnimationDeclarations {
- let sets = self.sets.read();
- let set = match sets.get(key) {
- Some(set) => set,
- None => return Default::default(),
- };
-
- let animations = set.get_value_map_for_active_animations(time).map(|map| {
- let block = PropertyDeclarationBlock::from_animation_value_map(&map);
- Arc::new(shared_lock.wrap(block))
- });
- let transitions = set.get_value_map_for_active_transitions(time).map(|map| {
- let block = PropertyDeclarationBlock::from_animation_value_map(&map);
- Arc::new(shared_lock.wrap(block))
- });
- AnimationDeclarations {
- animations,
- transitions,
- }
- }
-
- /// Cancel all animations for set at the given key.
- pub fn cancel_all_animations_for_key(&self, key: &AnimationSetKey) {
- if let Some(set) = self.sets.write().get_mut(key) {
- set.cancel_all_animations();
- }
- }
-}
-
-/// Kick off any new transitions for this node and return all of the properties that are
-/// transitioning. This is at the end of calculating style for a single node.
-pub fn start_transitions_if_applicable(
- context: &SharedStyleContext,
- old_style: &ComputedValues,
- new_style: &Arc<ComputedValues>,
- animation_state: &mut ElementAnimationSet,
-) -> LonghandIdSet {
- let mut properties_that_transition = LonghandIdSet::new();
- for transition in new_style.transition_properties() {
- let physical_property = transition.longhand_id.to_physical(new_style.writing_mode);
- if properties_that_transition.contains(physical_property) {
- continue;
- }
-
- properties_that_transition.insert(physical_property);
- animation_state.start_transition_if_applicable(
- context,
- physical_property,
- transition.index,
- old_style,
- new_style,
- );
- }
-
- properties_that_transition
-}
-
-/// Triggers animations for a given node looking at the animation property
-/// values.
-pub fn maybe_start_animations<E>(
- element: E,
- context: &SharedStyleContext,
- new_style: &Arc<ComputedValues>,
- animation_state: &mut ElementAnimationSet,
- resolver: &mut StyleResolverForElement<E>,
-) where
- E: TElement,
-{
- let style = new_style.get_ui();
- for (i, name) in style.animation_name_iter().enumerate() {
- let name = match name.as_atom() {
- Some(atom) => atom,
- None => continue,
- };
-
- debug!("maybe_start_animations: name={}", name);
- let duration = style.animation_duration_mod(i).seconds() as f64;
- if duration == 0. {
- continue;
- }
-
- let keyframe_animation = match context.stylist.get_animation(name, element) {
- Some(animation) => animation,
- None => continue,
- };
-
- debug!("maybe_start_animations: animation {} found", name);
-
- // If this animation doesn't have any keyframe, we can just continue
- // without submitting it to the compositor, since both the first and
- // the second keyframes would be synthetised from the computed
- // values.
- if keyframe_animation.steps.is_empty() {
- continue;
- }
-
- // NB: This delay may be negative, meaning that the animation may be created
- // in a state where we have advanced one or more iterations or even that the
- // animation begins in a finished state.
- let delay = style.animation_delay_mod(i).seconds();
-
- let iteration_count = style.animation_iteration_count_mod(i);
- let iteration_state = if iteration_count.0.is_infinite() {
- KeyframesIterationState::Infinite(0.0)
- } else {
- KeyframesIterationState::Finite(0.0, iteration_count.0 as f64)
- };
-
- let animation_direction = style.animation_direction_mod(i);
-
- let initial_direction = match animation_direction {
- AnimationDirection::Normal | AnimationDirection::Alternate => {
- AnimationDirection::Normal
- },
- AnimationDirection::Reverse | AnimationDirection::AlternateReverse => {
- AnimationDirection::Reverse
- },
- };
-
- let now = context.current_time_for_animations;
- let started_at = now + delay as f64;
- let mut starting_progress = (now - started_at) / duration;
- let state = match style.animation_play_state_mod(i) {
- AnimationPlayState::Paused => AnimationState::Paused(starting_progress),
- AnimationPlayState::Running => AnimationState::Pending,
- };
-
- let computed_steps = ComputedKeyframe::generate_for_keyframes(
- element,
- &keyframe_animation,
- context,
- new_style,
- style.animation_timing_function_mod(i),
- resolver,
- );
-
- let mut new_animation = Animation {
- name: name.clone(),
- properties_changed: keyframe_animation.properties_changed,
- computed_steps,
- started_at,
- duration,
- fill_mode: style.animation_fill_mode_mod(i),
- delay: delay as f64,
- iteration_state,
- state,
- direction: animation_direction,
- current_direction: initial_direction,
- cascade_style: new_style.clone(),
- is_new: true,
- };
-
- // If we started with a negative delay, make sure we iterate the animation if
- // the delay moves us past the first iteration.
- while starting_progress > 1. && !new_animation.on_last_iteration() {
- new_animation.iterate();
- starting_progress -= 1.;
- }
-
- animation_state.dirty = true;
-
- // If the animation was already present in the list for the node, just update its state.
- for existing_animation in animation_state.animations.iter_mut() {
- if existing_animation.state == AnimationState::Canceled {
- continue;
- }
-
- if new_animation.name == existing_animation.name {
- existing_animation
- .update_from_other(&new_animation, context.current_time_for_animations);
- return;
- }
- }
-
- animation_state.animations.push(new_animation);
- }
-}
diff --git a/components/style/applicable_declarations.rs b/components/style/applicable_declarations.rs
deleted file mode 100644
index a0dbb60da8e..00000000000
--- a/components/style/applicable_declarations.rs
+++ /dev/null
@@ -1,210 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
-
-//! Applicable declarations management.
-
-use crate::properties::PropertyDeclarationBlock;
-use crate::rule_tree::{CascadeLevel, StyleSource};
-use crate::shared_lock::Locked;
-use crate::stylesheets::layer_rule::LayerOrder;
-use servo_arc::Arc;
-use smallvec::SmallVec;
-
-/// List of applicable declarations. This is a transient structure that shuttles
-/// declarations between selector matching and inserting into the rule tree, and
-/// therefore we want to avoid heap-allocation where possible.
-///
-/// In measurements on wikipedia, we pretty much never have more than 8 applicable
-/// declarations, so we could consider making this 8 entries instead of 16.
-/// However, it may depend a lot on workload, and stack space is cheap.
-pub type ApplicableDeclarationList = SmallVec<[ApplicableDeclarationBlock; 16]>;
-
-/// Blink uses 18 bits to store source order, and does not check overflow [1].
-/// That's a limit that could be reached in realistic webpages, so we use
-/// 24 bits and enforce defined behavior in the overflow case.
-///
-/// Note that right now this restriction could be lifted if wanted (because we
-/// no longer stash the cascade level in the remaining bits), but we keep it in
-/// place in case we come up with a use-case for them, lacking reports of the
-/// current limit being too small.
-///
-/// [1] https://cs.chromium.org/chromium/src/third_party/WebKit/Source/core/css/
-/// RuleSet.h?l=128&rcl=90140ab80b84d0f889abc253410f44ed54ae04f3
-const SOURCE_ORDER_BITS: usize = 24;
-const SOURCE_ORDER_MAX: u32 = (1 << SOURCE_ORDER_BITS) - 1;
-const SOURCE_ORDER_MASK: u32 = SOURCE_ORDER_MAX;
-
-/// The cascade-level+layer order of this declaration.
-#[derive(Clone, Copy, Debug, Eq, Hash, MallocSizeOf, PartialEq)]
-pub struct CascadePriority {
- cascade_level: CascadeLevel,
- layer_order: LayerOrder,
-}
-
-const_assert_eq!(
- std::mem::size_of::<CascadePriority>(),
- std::mem::size_of::<u32>()
-);
-
-impl PartialOrd for CascadePriority {
- #[inline]
- fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
- Some(self.cmp(other))
- }
-}
-
-impl Ord for CascadePriority {
- fn cmp(&self, other: &Self) -> std::cmp::Ordering {
- self.cascade_level.cmp(&other.cascade_level).then_with(|| {
- let ordering = self.layer_order.cmp(&other.layer_order);
- if ordering == std::cmp::Ordering::Equal {
- return ordering;
- }
- // https://drafts.csswg.org/css-cascade-5/#cascade-layering
- //
- // Cascade layers (like declarations) are ordered by order
- // of appearance. When comparing declarations that belong to
- // different layers, then for normal rules the declaration
- // whose cascade layer is last wins, and for important rules
- // the declaration whose cascade layer is first wins.
- //
- // But the style attribute layer for some reason is special.
- if self.cascade_level.is_important() &&
- !self.layer_order.is_style_attribute_layer() &&
- !other.layer_order.is_style_attribute_layer()
- {
- ordering.reverse()
- } else {
- ordering
- }
- })
- }
-}
-
-impl CascadePriority {
- /// Construct a new CascadePriority for a given (level, order) pair.
- pub fn new(cascade_level: CascadeLevel, layer_order: LayerOrder) -> Self {
- Self {
- cascade_level,
- layer_order,
- }
- }
-
- /// Returns the layer order.
- #[inline]
- pub fn layer_order(&self) -> LayerOrder {
- self.layer_order
- }
-
- /// Returns the cascade level.
- #[inline]
- pub fn cascade_level(&self) -> CascadeLevel {
- self.cascade_level
- }
-
- /// Whether this declaration should be allowed if `revert` or `revert-layer`
- /// have been specified on a given origin.
- ///
- /// `self` is the priority at which the `revert` or `revert-layer` keyword
- /// have been specified.
- pub fn allows_when_reverted(&self, other: &Self, origin_revert: bool) -> bool {
- if origin_revert {
- other.cascade_level.origin() < self.cascade_level.origin()
- } else {
- other.unimportant() < self.unimportant()
- }
- }
-
- /// Convert this priority from "important" to "non-important", if needed.
- pub fn unimportant(&self) -> Self {
- Self::new(self.cascade_level().unimportant(), self.layer_order())
- }
-
- /// Convert this priority from "non-important" to "important", if needed.
- pub fn important(&self) -> Self {
- Self::new(self.cascade_level().important(), self.layer_order())
- }
-}
-
-/// A property declaration together with its precedence among rules of equal
-/// specificity so that we can sort them.
-///
-/// This represents the declarations in a given declaration block for a given
-/// importance.
-#[derive(Clone, Debug, MallocSizeOf, PartialEq)]
-pub struct ApplicableDeclarationBlock {
- /// The style source, either a style rule, or a property declaration block.
- #[ignore_malloc_size_of = "Arc"]
- pub source: StyleSource,
- /// The bits containing the source order, cascade level, and shadow cascade
- /// order.
- source_order: u32,
- /// The specificity of the selector.
- pub specificity: u32,
- /// The cascade priority of the rule.
- pub cascade_priority: CascadePriority,
-}
-
-impl ApplicableDeclarationBlock {
- /// Constructs an applicable declaration block from a given property
- /// declaration block and importance.
- #[inline]
- pub fn from_declarations(
- declarations: Arc<Locked<PropertyDeclarationBlock>>,
- level: CascadeLevel,
- layer_order: LayerOrder,
- ) -> Self {
- ApplicableDeclarationBlock {
- source: StyleSource::from_declarations(declarations),
- source_order: 0,
- specificity: 0,
- cascade_priority: CascadePriority::new(level, layer_order),
- }
- }
-
- /// Constructs an applicable declaration block from the given components.
- #[inline]
- pub fn new(
- source: StyleSource,
- source_order: u32,
- level: CascadeLevel,
- specificity: u32,
- layer_order: LayerOrder,
- ) -> Self {
- ApplicableDeclarationBlock {
- source,
- source_order: source_order & SOURCE_ORDER_MASK,
- specificity,
- cascade_priority: CascadePriority::new(level, layer_order),
- }
- }
-
- /// Returns the source order of the block.
- #[inline]
- pub fn source_order(&self) -> u32 {
- self.source_order
- }
-
- /// Returns the cascade level of the block.
- #[inline]
- pub fn level(&self) -> CascadeLevel {
- self.cascade_priority.cascade_level()
- }
-
- /// Returns the cascade level of the block.
- #[inline]
- pub fn layer_order(&self) -> LayerOrder {
- self.cascade_priority.layer_order()
- }
-
- /// Convenience method to consume self and return the right thing for the
- /// rule tree to iterate over.
- #[inline]
- pub fn for_rule_tree(self) -> (StyleSource, CascadePriority) {
- (self.source, self.cascade_priority)
- }
-}
-
-// Size of this struct determines sorting and selector-matching performance.
-size_of_test!(ApplicableDeclarationBlock, 24);
diff --git a/components/style/attr.rs b/components/style/attr.rs
deleted file mode 100644
index 7747921ffe9..00000000000
--- a/components/style/attr.rs
+++ /dev/null
@@ -1,601 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
-
-//! Parsed representations of [DOM attributes][attr].
-//!
-//! [attr]: https://dom.spec.whatwg.org/#interface-attr
-
-use crate::properties::PropertyDeclarationBlock;
-use crate::shared_lock::Locked;
-use crate::str::str_join;
-use crate::str::{read_exponent, read_fraction, HTML_SPACE_CHARACTERS};
-use crate::str::{read_numbers, split_commas, split_html_space_chars};
-use crate::values::specified::Length;
-use crate::values::AtomString;
-use crate::{Atom, LocalName, Namespace, Prefix};
-use app_units::Au;
-use cssparser::{self, Color, RGBA};
-use euclid::num::Zero;
-use num_traits::ToPrimitive;
-use selectors::attr::AttrSelectorOperation;
-use servo_arc::Arc;
-use std::str::FromStr;
-
-// Duplicated from script::dom::values.
-const UNSIGNED_LONG_MAX: u32 = 2147483647;
-
-#[derive(Clone, Copy, Debug, PartialEq)]
-#[cfg_attr(feature = "servo", derive(MallocSizeOf))]
-pub enum LengthOrPercentageOrAuto {
- Auto,
- Percentage(f32),
- Length(Au),
-}
-
-#[derive(Clone, Debug)]
-#[cfg_attr(feature = "servo", derive(MallocSizeOf))]
-pub enum AttrValue {
- String(String),
- TokenList(String, Vec<Atom>),
- UInt(String, u32),
- Int(String, i32),
- Double(String, f64),
- Atom(Atom),
- Length(String, Option<Length>),
- Color(String, Option<RGBA>),
- Dimension(String, LengthOrPercentageOrAuto),
-
- /// Stores a URL, computed from the input string and a document's base URL.
- ///
- /// The URL is resolved at setting-time, so this kind of attribute value is
- /// not actually suitable for most URL-reflecting IDL attributes.
- ResolvedUrl(
- String,
- #[ignore_malloc_size_of = "Arc"] Option<Arc<url::Url>>
- ),
-
- /// Note that this variant is only used transitively as a fast path to set
- /// the property declaration block relevant to the style of an element when
- /// set from the inline declaration of that element (that is,
- /// `element.style`).
- ///
- /// This can, as of this writing, only correspond to the value of the
- /// `style` element, and is set from its relevant CSSInlineStyleDeclaration,
- /// and then converted to a string in Element::attribute_mutated.
- ///
- /// Note that we don't necessarily need to do that (we could just clone the
- /// declaration block), but that avoids keeping a refcounted
- /// declarationblock for longer than needed.
- Declaration(
- String,
- #[ignore_malloc_size_of = "Arc"] Arc<Locked<PropertyDeclarationBlock>>,
- ),
-}
-
-/// Shared implementation to parse an integer according to
-/// <https://html.spec.whatwg.org/multipage/#rules-for-parsing-integers> or
-/// <https://html.spec.whatwg.org/multipage/#rules-for-parsing-non-negative-integers>
-fn do_parse_integer<T: Iterator<Item = char>>(input: T) -> Result<i64, ()> {
- let mut input = input
- .skip_while(|c| HTML_SPACE_CHARACTERS.iter().any(|s| s == c))
- .peekable();
-
- let sign = match input.peek() {
- None => return Err(()),
- Some(&'-') => {
- input.next();
- -1
- },
- Some(&'+') => {
- input.next();
- 1
- },
- Some(_) => 1,
- };
-
- let (value, _) = read_numbers(input);
-
- value.and_then(|value| value.checked_mul(sign)).ok_or(())
-}
-
-/// Parse an integer according to
-/// <https://html.spec.whatwg.org/multipage/#rules-for-parsing-integers>.
-pub fn parse_integer<T: Iterator<Item = char>>(input: T) -> Result<i32, ()> {
- do_parse_integer(input).and_then(|result| result.to_i32().ok_or(()))
-}
-
-/// Parse an integer according to
-/// <https://html.spec.whatwg.org/multipage/#rules-for-parsing-non-negative-integers>
-pub fn parse_unsigned_integer<T: Iterator<Item = char>>(input: T) -> Result<u32, ()> {
- do_parse_integer(input).and_then(|result| result.to_u32().ok_or(()))
-}
-
-/// Parse a floating-point number according to
-/// <https://html.spec.whatwg.org/multipage/#rules-for-parsing-floating-point-number-values>
-pub fn parse_double(string: &str) -> Result<f64, ()> {
- let trimmed = string.trim_matches(HTML_SPACE_CHARACTERS);
- let mut input = trimmed.chars().peekable();
-
- let (value, divisor, chars_skipped) = match input.peek() {
- None => return Err(()),
- Some(&'-') => {
- input.next();
- (-1f64, -1f64, 1)
- },
- Some(&'+') => {
- input.next();
- (1f64, 1f64, 1)
- },
- _ => (1f64, 1f64, 0),
- };
-
- let (value, value_digits) = if let Some(&'.') = input.peek() {
- (0f64, 0)
- } else {
- let (read_val, read_digits) = read_numbers(input);
- (
- value * read_val.and_then(|result| result.to_f64()).unwrap_or(1f64),
- read_digits,
- )
- };
-
- let input = trimmed
- .chars()
- .skip(value_digits + chars_skipped)
- .peekable();
-
- let (mut value, fraction_digits) = read_fraction(input, divisor, value);
-
- let input = trimmed
- .chars()
- .skip(value_digits + chars_skipped + fraction_digits)
- .peekable();
-
- if let Some(exp) = read_exponent(input) {
- value *= 10f64.powi(exp)
- };
-
- Ok(value)
-}
-
-impl AttrValue {
- pub fn from_serialized_tokenlist(tokens: String) -> AttrValue {
- let atoms =
- split_html_space_chars(&tokens)
- .map(Atom::from)
- .fold(vec![], |mut acc, atom| {
- if !acc.contains(&atom) {
- acc.push(atom)
- }
- acc
- });
- AttrValue::TokenList(tokens, atoms)
- }
-
- pub fn from_comma_separated_tokenlist(tokens: String) -> AttrValue {
- let atoms = split_commas(&tokens)
- .map(Atom::from)
- .fold(vec![], |mut acc, atom| {
- if !acc.contains(&atom) {
- acc.push(atom)
- }
- acc
- });
- AttrValue::TokenList(tokens, atoms)
- }
-
- pub fn from_atomic_tokens(atoms: Vec<Atom>) -> AttrValue {
- // TODO(ajeffrey): effecient conversion of Vec<Atom> to String
- let tokens = String::from(str_join(&atoms, "\x20"));
- AttrValue::TokenList(tokens, atoms)
- }
-
- // https://html.spec.whatwg.org/multipage/#reflecting-content-attributes-in-idl-attributes:idl-unsigned-long
- pub fn from_u32(string: String, default: u32) -> AttrValue {
- let result = parse_unsigned_integer(string.chars()).unwrap_or(default);
- let result = if result > UNSIGNED_LONG_MAX {
- default
- } else {
- result
- };
- AttrValue::UInt(string, result)
- }
-
- pub fn from_i32(string: String, default: i32) -> AttrValue {
- let result = parse_integer(string.chars()).unwrap_or(default);
- AttrValue::Int(string, result)
- }
-
- // https://html.spec.whatwg.org/multipage/#reflecting-content-attributes-in-idl-attributes:idl-double
- pub fn from_double(string: String, default: f64) -> AttrValue {
- let result = parse_double(&string).unwrap_or(default);
-
- if result.is_normal() {
- AttrValue::Double(string, result)
- } else {
- AttrValue::Double(string, default)
- }
- }
-
- // https://html.spec.whatwg.org/multipage/#limited-to-only-non-negative-numbers
- pub fn from_limited_i32(string: String, default: i32) -> AttrValue {
- let result = parse_integer(string.chars()).unwrap_or(default);
-
- if result < 0 {
- AttrValue::Int(string, default)
- } else {
- AttrValue::Int(string, result)
- }
- }
-
- // https://html.spec.whatwg.org/multipage/#limited-to-only-non-negative-numbers-greater-than-zero
- pub fn from_limited_u32(string: String, default: u32) -> AttrValue {
- let result = parse_unsigned_integer(string.chars()).unwrap_or(default);
- let result = if result == 0 || result > UNSIGNED_LONG_MAX {
- default
- } else {
- result
- };
- AttrValue::UInt(string, result)
- }
-
- pub fn from_atomic(string: String) -> AttrValue {
- let value = Atom::from(string);
- AttrValue::Atom(value)
- }
-
- pub fn from_resolved_url(base: &Arc<::url::Url>, url: String) -> AttrValue {
- let joined = base.join(&url).ok().map(Arc::new);
- AttrValue::ResolvedUrl(url, joined)
- }
-
- pub fn from_legacy_color(string: String) -> AttrValue {
- let parsed = parse_legacy_color(&string).ok();
- AttrValue::Color(string, parsed)
- }
-
- pub fn from_dimension(string: String) -> AttrValue {
- let parsed = parse_length(&string);
- AttrValue::Dimension(string, parsed)
- }
-
- pub fn from_nonzero_dimension(string: String) -> AttrValue {
- let parsed = parse_nonzero_length(&string);
- AttrValue::Dimension(string, parsed)
- }
-
- /// Assumes the `AttrValue` is a `TokenList` and returns its tokens
- ///
- /// ## Panics
- ///
- /// Panics if the `AttrValue` is not a `TokenList`
- pub fn as_tokens(&self) -> &[Atom] {
- match *self {
- AttrValue::TokenList(_, ref tokens) => tokens,
- _ => panic!("Tokens not found"),
- }
- }
-
- /// Assumes the `AttrValue` is an `Atom` and returns its value
- ///
- /// ## Panics
- ///
- /// Panics if the `AttrValue` is not an `Atom`
- pub fn as_atom(&self) -> &Atom {
- match *self {
- AttrValue::Atom(ref value) => value,
- _ => panic!("Atom not found"),
- }
- }
-
- /// Assumes the `AttrValue` is a `Color` and returns its value
- ///
- /// ## Panics
- ///
- /// Panics if the `AttrValue` is not a `Color`
- pub fn as_color(&self) -> Option<&RGBA> {
- match *self {
- AttrValue::Color(_, ref color) => color.as_ref(),
- _ => panic!("Color not found"),
- }
- }
-
- /// Assumes the `AttrValue` is a `Dimension` and returns its value
- ///
- /// ## Panics
- ///
- /// Panics if the `AttrValue` is not a `Dimension`
- pub fn as_dimension(&self) -> &LengthOrPercentageOrAuto {
- match *self {
- AttrValue::Dimension(_, ref l) => l,
- _ => panic!("Dimension not found"),
- }
- }
-
- /// Assumes the `AttrValue` is a `ResolvedUrl` and returns its value.
- ///
- /// ## Panics
- ///
- /// Panics if the `AttrValue` is not a `ResolvedUrl`
- pub fn as_resolved_url(&self) -> Option<&Arc<::url::Url>> {
- match *self {
- AttrValue::ResolvedUrl(_, ref url) => url.as_ref(),
- _ => panic!("Url not found"),
- }
- }
-
- /// Return the AttrValue as its integer representation, if any.
- /// This corresponds to attribute values returned as `AttrValue::UInt(_)`
- /// by `VirtualMethods::parse_plain_attribute()`.
- ///
- /// ## Panics
- ///
- /// Panics if the `AttrValue` is not a `UInt`
- pub fn as_uint(&self) -> u32 {
- if let AttrValue::UInt(_, value) = *self {
- value
- } else {
- panic!("Uint not found");
- }
- }
-
- /// Return the AttrValue as a dimension computed from its integer
- /// representation, assuming that integer representation specifies pixels.
- ///
- /// This corresponds to attribute values returned as `AttrValue::UInt(_)`
- /// by `VirtualMethods::parse_plain_attribute()`.
- ///
- /// ## Panics
- ///
- /// Panics if the `AttrValue` is not a `UInt`
- pub fn as_uint_px_dimension(&self) -> LengthOrPercentageOrAuto {
- if let AttrValue::UInt(_, value) = *self {
- LengthOrPercentageOrAuto::Length(Au::from_px(value as i32))
- } else {
- panic!("Uint not found");
- }
- }
-
- pub fn eval_selector(&self, selector: &AttrSelectorOperation<&AtomString>) -> bool {
- // FIXME(SimonSapin) this can be more efficient by matching on `(self, selector)` variants
- // and doing Atom comparisons instead of string comparisons where possible,
- // with SelectorImpl::AttrValue changed to Atom.
- selector.eval_str(self)
- }
-}
-
-impl ::std::ops::Deref for AttrValue {
- type Target = str;
-
- fn deref(&self) -> &str {
- match *self {
- AttrValue::String(ref value) |
- AttrValue::TokenList(ref value, _) |
- AttrValue::UInt(ref value, _) |
- AttrValue::Double(ref value, _) |
- AttrValue::Length(ref value, _) |
- AttrValue::Color(ref value, _) |
- AttrValue::Int(ref value, _) |
- AttrValue::ResolvedUrl(ref value, _) |
- AttrValue::Declaration(ref value, _) |
- AttrValue::Dimension(ref value, _) => &value,
- AttrValue::Atom(ref value) => &value,
- }
- }
-}
-
-impl PartialEq<Atom> for AttrValue {
- fn eq(&self, other: &Atom) -> bool {
- match *self {
- AttrValue::Atom(ref value) => value == other,
- _ => other == &**self,
- }
- }
-}
-
-/// <https://html.spec.whatwg.org/multipage/#rules-for-parsing-non-zero-dimension-values>
-pub fn parse_nonzero_length(value: &str) -> LengthOrPercentageOrAuto {
- match parse_length(value) {
- LengthOrPercentageOrAuto::Length(x) if x == Au::zero() => LengthOrPercentageOrAuto::Auto,
- LengthOrPercentageOrAuto::Percentage(x) if x == 0. => LengthOrPercentageOrAuto::Auto,
- x => x,
- }
-}
-
-/// Parses a [legacy color][color]. If unparseable, `Err` is returned.
-///
-/// [color]: https://html.spec.whatwg.org/multipage/#rules-for-parsing-a-legacy-colour-value
-pub fn parse_legacy_color(mut input: &str) -> Result<RGBA, ()> {
- // Steps 1 and 2.
- if input.is_empty() {
- return Err(());
- }
-
- // Step 3.
- input = input.trim_matches(HTML_SPACE_CHARACTERS);
-
- // Step 4.
- if input.eq_ignore_ascii_case("transparent") {
- return Err(());
- }
-
- // Step 5.
- if let Ok(Color::Rgba(rgba)) = cssparser::parse_color_keyword(input) {
- return Ok(rgba);
- }
-
- // Step 6.
- if input.len() == 4 {
- if let (b'#', Ok(r), Ok(g), Ok(b)) = (
- input.as_bytes()[0],
- hex(input.as_bytes()[1] as char),
- hex(input.as_bytes()[2] as char),
- hex(input.as_bytes()[3] as char),
- ) {
- return Ok(RGBA::new(Some(r * 17), Some(g * 17), Some(b * 17), Some(1.0)));
- }
- }
-
- // Step 7.
- let mut new_input = String::new();
- for ch in input.chars() {
- if ch as u32 > 0xffff {
- new_input.push_str("00")
- } else {
- new_input.push(ch)
- }
- }
- let mut input = &*new_input;
-
- // Step 8.
- for (char_count, (index, _)) in input.char_indices().enumerate() {
- if char_count == 128 {
- input = &input[..index];
- break;
- }
- }
-
- // Step 9.
- if input.as_bytes()[0] == b'#' {
- input = &input[1..]
- }
-
- // Step 10.
- let mut new_input = Vec::new();
- for ch in input.chars() {
- if hex(ch).is_ok() {
- new_input.push(ch as u8)
- } else {
- new_input.push(b'0')
- }
- }
- let mut input = new_input;
-
- // Step 11.
- while input.is_empty() || (input.len() % 3) != 0 {
- input.push(b'0')
- }
-
- // Step 12.
- let mut length = input.len() / 3;
- let (mut red, mut green, mut blue) = (
- &input[..length],
- &input[length..length * 2],
- &input[length * 2..],
- );
-
- // Step 13.
- if length > 8 {
- red = &red[length - 8..];
- green = &green[length - 8..];
- blue = &blue[length - 8..];
- length = 8
- }
-
- // Step 14.
- while length > 2 && red[0] == b'0' && green[0] == b'0' && blue[0] == b'0' {
- red = &red[1..];
- green = &green[1..];
- blue = &blue[1..];
- length -= 1
- }
-
- // Steps 15-20.
- return Ok(RGBA::new(
- Some(hex_string(red).unwrap()),
- Some(hex_string(green).unwrap()),
- Some(hex_string(blue).unwrap()),
- Some(1.0),
- ));
-
- fn hex(ch: char) -> Result<u8, ()> {
- match ch {
- '0'..='9' => Ok((ch as u8) - b'0'),
- 'a'..='f' => Ok((ch as u8) - b'a' + 10),
- 'A'..='F' => Ok((ch as u8) - b'A' + 10),
- _ => Err(()),
- }
- }
-
- fn hex_string(string: &[u8]) -> Result<u8, ()> {
- match string.len() {
- 0 => Err(()),
- 1 => hex(string[0] as char),
- _ => {
- let upper = hex(string[0] as char)?;
- let lower = hex(string[1] as char)?;
- Ok((upper << 4) | lower)
- },
- }
- }
-}
-
-/// Parses a [dimension value][dim]. If unparseable, `Auto` is returned.
-///
-/// [dim]: https://html.spec.whatwg.org/multipage/#rules-for-parsing-dimension-values
-// TODO: this function can be rewritten to return Result<LengthPercentage, _>
-pub fn parse_length(mut value: &str) -> LengthOrPercentageOrAuto {
- // Steps 1 & 2 are not relevant
-
- // Step 3
- value = value.trim_start_matches(HTML_SPACE_CHARACTERS);
-
- // Step 4
- match value.chars().nth(0) {
- Some('0'..='9') => {},
- _ => return LengthOrPercentageOrAuto::Auto,
- }
-
- // Steps 5 to 8
- // We trim the string length to the minimum of:
- // 1. the end of the string
- // 2. the first occurence of a '%' (U+0025 PERCENT SIGN)
- // 3. the second occurrence of a '.' (U+002E FULL STOP)
- // 4. the occurrence of a character that is neither a digit nor '%' nor '.'
- // Note: Step 7.4 is directly subsumed by FromStr::from_str
- let mut end_index = value.len();
- let (mut found_full_stop, mut found_percent) = (false, false);
- for (i, ch) in value.chars().enumerate() {
- match ch {
- '0'..='9' => continue,
- '%' => {
- found_percent = true;
- end_index = i;
- break;
- },
- '.' if !found_full_stop => {
- found_full_stop = true;
- continue;
- },
- _ => {
- end_index = i;
- break;
- },
- }
- }
- value = &value[..end_index];
-
- if found_percent {
- let result: Result<f32, _> = FromStr::from_str(value);
- match result {
- Ok(number) => return LengthOrPercentageOrAuto::Percentage((number as f32) / 100.0),
- Err(_) => return LengthOrPercentageOrAuto::Auto,
- }
- }
-
- match FromStr::from_str(value) {
- Ok(number) => LengthOrPercentageOrAuto::Length(Au::from_f64_px(number)),
- Err(_) => LengthOrPercentageOrAuto::Auto,
- }
-}
-
-/// A struct that uniquely identifies an element's attribute.
-#[derive(Clone, Debug)]
-#[cfg_attr(feature = "servo", derive(MallocSizeOf))]
-pub struct AttrIdentifier {
- pub local_name: LocalName,
- pub name: LocalName,
- pub namespace: Namespace,
- pub prefix: Option<Prefix>,
-}
diff --git a/components/style/author_styles.rs b/components/style/author_styles.rs
deleted file mode 100644
index a0223dceccc..00000000000
--- a/components/style/author_styles.rs
+++ /dev/null
@@ -1,70 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
-
-//! A set of author stylesheets and their computed representation, such as the
-//! ones used for ShadowRoot.
-
-use crate::dom::TElement;
-use crate::invalidation::media_queries::ToMediaListKey;
-use crate::shared_lock::SharedRwLockReadGuard;
-use crate::stylesheet_set::AuthorStylesheetSet;
-use crate::stylesheets::StylesheetInDocument;
-use crate::stylist::CascadeData;
-use crate::stylist::Stylist;
-use servo_arc::Arc;
-
-/// A set of author stylesheets and their computed representation, such as the
-/// ones used for ShadowRoot.
-#[derive(MallocSizeOf)]
-pub struct GenericAuthorStyles<S>
-where
- S: StylesheetInDocument + PartialEq + 'static,
-{
- /// The sheet collection, which holds the sheet pointers, the invalidations,
- /// and all that stuff.
- pub stylesheets: AuthorStylesheetSet<S>,
- /// The actual cascade data computed from the stylesheets.
- #[ignore_malloc_size_of = "Measured as part of the stylist"]
- pub data: Arc<CascadeData>,
-}
-
-pub use self::GenericAuthorStyles as AuthorStyles;
-
-lazy_static! {
- static ref EMPTY_CASCADE_DATA: Arc<CascadeData> = Arc::new_leaked(CascadeData::new());
-}
-
-impl<S> GenericAuthorStyles<S>
-where
- S: StylesheetInDocument + PartialEq + 'static,
-{
- /// Create an empty AuthorStyles.
- #[inline]
- pub fn new() -> Self {
- Self {
- stylesheets: AuthorStylesheetSet::new(),
- data: EMPTY_CASCADE_DATA.clone(),
- }
- }
-
- /// Flush the pending sheet changes, updating `data` as appropriate.
- ///
- /// TODO(emilio): Need a host element and a snapshot map to do invalidation
- /// properly.
- #[inline]
- pub fn flush<E>(&mut self, stylist: &mut Stylist, guard: &SharedRwLockReadGuard)
- where
- E: TElement,
- S: ToMediaListKey,
- {
- let flusher = self
- .stylesheets
- .flush::<E>(/* host = */ None, /* snapshot_map = */ None);
-
- let result = stylist.rebuild_author_data(&self.data, flusher.sheets, guard);
- if let Ok(Some(new_data)) = result {
- self.data = new_data;
- }
- }
-}
diff --git a/components/style/bezier.rs b/components/style/bezier.rs
deleted file mode 100644
index dd520ac0ed5..00000000000
--- a/components/style/bezier.rs
+++ /dev/null
@@ -1,176 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
-
-//! Parametric Bézier curves.
-//!
-//! This is based on `WebCore/platform/graphics/UnitBezier.h` in WebKit.
-
-#![deny(missing_docs)]
-
-use crate::values::CSSFloat;
-
-const NEWTON_METHOD_ITERATIONS: u8 = 8;
-
-/// A unit cubic Bézier curve, used for timing functions in CSS transitions and animations.
-pub struct Bezier {
- ax: f64,
- bx: f64,
- cx: f64,
- ay: f64,
- by: f64,
- cy: f64,
-}
-
-impl Bezier {
- /// Calculate the output of a unit cubic Bézier curve from the two middle control points.
- ///
- /// X coordinate is time, Y coordinate is function advancement.
- /// The nominal range for both is 0 to 1.
- ///
- /// The start and end points are always (0, 0) and (1, 1) so that a transition or animation
- /// starts at 0% and ends at 100%.
- pub fn calculate_bezier_output(
- progress: f64,
- epsilon: f64,
- x1: f32,
- y1: f32,
- x2: f32,
- y2: f32,
- ) -> f64 {
- // Check for a linear curve.
- if x1 == y1 && x2 == y2 {
- return progress;
- }
-
- // Ensure that we return 0 or 1 on both edges.
- if progress == 0.0 {
- return 0.0;
- }
- if progress == 1.0 {
- return 1.0;
- }
-
- // For negative values, try to extrapolate with tangent (p1 - p0) or,
- // if p1 is coincident with p0, with (p2 - p0).
- if progress < 0.0 {
- if x1 > 0.0 {
- return progress * y1 as f64 / x1 as f64;
- }
- if y1 == 0.0 && x2 > 0.0 {
- return progress * y2 as f64 / x2 as f64;
- }
- // If we can't calculate a sensible tangent, don't extrapolate at all.
- return 0.0;
- }
-
- // For values greater than 1, try to extrapolate with tangent (p2 - p3) or,
- // if p2 is coincident with p3, with (p1 - p3).
- if progress > 1.0 {
- if x2 < 1.0 {
- return 1.0 + (progress - 1.0) * (y2 as f64 - 1.0) / (x2 as f64 - 1.0);
- }
- if y2 == 1.0 && x1 < 1.0 {
- return 1.0 + (progress - 1.0) * (y1 as f64 - 1.0) / (x1 as f64 - 1.0);
- }
- // If we can't calculate a sensible tangent, don't extrapolate at all.
- return 1.0;
- }
-
- Bezier::new(x1, y1, x2, y2).solve(progress, epsilon)
- }
-
- #[inline]
- fn new(x1: CSSFloat, y1: CSSFloat, x2: CSSFloat, y2: CSSFloat) -> Bezier {
- let cx = 3. * x1 as f64;
- let bx = 3. * (x2 as f64 - x1 as f64) - cx;
-
- let cy = 3. * y1 as f64;
- let by = 3. * (y2 as f64 - y1 as f64) - cy;
-
- Bezier {
- ax: 1.0 - cx - bx,
- bx: bx,
- cx: cx,
- ay: 1.0 - cy - by,
- by: by,
- cy: cy,
- }
- }
-
- #[inline]
- fn sample_curve_x(&self, t: f64) -> f64 {
- // ax * t^3 + bx * t^2 + cx * t
- ((self.ax * t + self.bx) * t + self.cx) * t
- }
-
- #[inline]
- fn sample_curve_y(&self, t: f64) -> f64 {
- ((self.ay * t + self.by) * t + self.cy) * t
- }
-
- #[inline]
- fn sample_curve_derivative_x(&self, t: f64) -> f64 {
- (3.0 * self.ax * t + 2.0 * self.bx) * t + self.cx
- }
-
- #[inline]
- fn solve_curve_x(&self, x: f64, epsilon: f64) -> f64 {
- // Fast path: Use Newton's method.
- let mut t = x;
- for _ in 0..NEWTON_METHOD_ITERATIONS {
- let x2 = self.sample_curve_x(t);
- if x2.approx_eq(x, epsilon) {
- return t;
- }
- let dx = self.sample_curve_derivative_x(t);
- if dx.approx_eq(0.0, 1e-6) {
- break;
- }
- t -= (x2 - x) / dx;
- }
-
- // Slow path: Use bisection.
- let (mut lo, mut hi, mut t) = (0.0, 1.0, x);
-
- if t < lo {
- return lo;
- }
- if t > hi {
- return hi;
- }
-
- while lo < hi {
- let x2 = self.sample_curve_x(t);
- if x2.approx_eq(x, epsilon) {
- return t;
- }
- if x > x2 {
- lo = t
- } else {
- hi = t
- }
- t = (hi - lo) / 2.0 + lo
- }
-
- t
- }
-
- /// Solve the bezier curve for a given `x` and an `epsilon`, that should be
- /// between zero and one.
- #[inline]
- fn solve(&self, x: f64, epsilon: f64) -> f64 {
- self.sample_curve_y(self.solve_curve_x(x, epsilon))
- }
-}
-
-trait ApproxEq {
- fn approx_eq(self, value: Self, epsilon: Self) -> bool;
-}
-
-impl ApproxEq for f64 {
- #[inline]
- fn approx_eq(self, value: f64, epsilon: f64) -> bool {
- (self - value).abs() < epsilon
- }
-}
diff --git a/components/style/bloom.rs b/components/style/bloom.rs
deleted file mode 100644
index dc722b7bdba..00000000000
--- a/components/style/bloom.rs
+++ /dev/null
@@ -1,401 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
-
-//! The style bloom filter is used as an optimization when matching deep
-//! descendant selectors.
-
-#![deny(missing_docs)]
-
-use crate::dom::{SendElement, TElement};
-use crate::LocalName;
-use atomic_refcell::{AtomicRefCell, AtomicRefMut};
-use owning_ref::OwningHandle;
-use selectors::bloom::BloomFilter;
-use servo_arc::Arc;
-use smallvec::SmallVec;
-use std::mem::ManuallyDrop;
-
-thread_local! {
- /// Bloom filters are large allocations, so we store them in thread-local storage
- /// such that they can be reused across style traversals. StyleBloom is responsible
- /// for ensuring that the bloom filter is zeroed when it is dropped.
- ///
- /// We intentionally leak this from TLS because we don't have the guarantee
- /// of TLS destructors to run in worker threads.
- ///
- /// We could change this once https://github.com/rayon-rs/rayon/issues/688
- /// is fixed, hopefully.
- static BLOOM_KEY: ManuallyDrop<Arc<AtomicRefCell<BloomFilter>>> =
- ManuallyDrop::new(Arc::new_leaked(Default::default()));
-}
-
-/// A struct that allows us to fast-reject deep descendant selectors avoiding
-/// selector-matching.
-///
-/// This is implemented using a counting bloom filter, and it's a standard
-/// optimization. See Gecko's `AncestorFilter`, and Blink's and WebKit's
-/// `SelectorFilter`.
-///
-/// The constraints for Servo's style system are a bit different compared to
-/// traditional style systems given Servo does a parallel breadth-first
-/// traversal instead of a sequential depth-first traversal.
-///
-/// This implies that we need to track a bit more state than other browsers to
-/// ensure we're doing the correct thing during the traversal, and being able to
-/// apply this optimization effectively.
-///
-/// Concretely, we have a bloom filter instance per worker thread, and we track
-/// the current DOM depth in order to find a common ancestor when it doesn't
-/// match the previous element we've styled.
-///
-/// This is usually a pretty fast operation (we use to be one level deeper than
-/// the previous one), but in the case of work-stealing, we may needed to push
-/// and pop multiple elements.
-///
-/// See the `insert_parents_recovering`, where most of the magic happens.
-///
-/// Regarding thread-safety, this struct is safe because:
-///
-/// * We clear this after a restyle.
-/// * The DOM shape and attributes (and every other thing we access here) are
-/// immutable during a restyle.
-///
-pub struct StyleBloom<E: TElement> {
- /// A handle to the bloom filter from the thread upon which this StyleBloom
- /// was created. We use AtomicRefCell so that this is all |Send|, which allows
- /// StyleBloom to live in ThreadLocalStyleContext, which is dropped from the
- /// parent thread.
- filter: OwningHandle<Arc<AtomicRefCell<BloomFilter>>, AtomicRefMut<'static, BloomFilter>>,
-
- /// The stack of elements that this bloom filter contains, along with the
- /// number of hashes pushed for each element.
- elements: SmallVec<[PushedElement<E>; 16]>,
-
- /// Stack of hashes that have been pushed onto this filter.
- pushed_hashes: SmallVec<[u32; 64]>,
-}
-
-/// The very rough benchmarks in the selectors crate show clear()
-/// costing about 25 times more than remove_hash(). We use this to implement
-/// clear() more efficiently when only a small number of hashes have been
-/// pushed.
-///
-/// One subtly to note is that remove_hash() will not touch the value
-/// if the filter overflowed. However, overflow can only occur if we
-/// get 255 collisions on the same hash value, and 25 < 255.
-const MEMSET_CLEAR_THRESHOLD: usize = 25;
-
-struct PushedElement<E: TElement> {
- /// The element that was pushed.
- element: SendElement<E>,
-
- /// The number of hashes pushed for the element.
- num_hashes: usize,
-}
-
-impl<E: TElement> PushedElement<E> {
- fn new(el: E, num_hashes: usize) -> Self {
- PushedElement {
- element: unsafe { SendElement::new(el) },
- num_hashes,
- }
- }
-}
-
-/// Returns whether the attribute name is excluded from the bloom filter.
-///
-/// We do this for attributes that are very common but not commonly used in
-/// selectors.
-#[inline]
-pub fn is_attr_name_excluded_from_filter(name: &LocalName) -> bool {
- return *name == local_name!("class") || *name == local_name!("id") || *name == local_name!("style")
-}
-
-fn each_relevant_element_hash<E, F>(element: E, mut f: F)
-where
- E: TElement,
- F: FnMut(u32),
-{
- f(element.local_name().get_hash());
- f(element.namespace().get_hash());
-
- if let Some(id) = element.id() {
- f(id.get_hash());
- }
-
- element.each_class(|class| f(class.get_hash()));
-
- element.each_attr_name(|name| {
- if !is_attr_name_excluded_from_filter(name) {
- f(name.get_hash())
- }
- });
-}
-
-impl<E: TElement> Drop for StyleBloom<E> {
- fn drop(&mut self) {
- // Leave the reusable bloom filter in a zeroed state.
- self.clear();
- }
-}
-
-impl<E: TElement> StyleBloom<E> {
- /// Create an empty `StyleBloom`. Because StyleBloom acquires the thread-
- /// local filter buffer, creating multiple live StyleBloom instances at
- /// the same time on the same thread will panic.
-
- // Forced out of line to limit stack frame sizes after extra inlining from
- // https://github.com/rust-lang/rust/pull/43931
- //
- // See https://github.com/servo/servo/pull/18420#issuecomment-328769322
- #[inline(never)]
- pub fn new() -> Self {
- let bloom_arc = BLOOM_KEY.with(|b| Arc::clone(&*b));
- let filter =
- OwningHandle::new_with_fn(bloom_arc, |x| unsafe { x.as_ref() }.unwrap().borrow_mut());
- debug_assert!(
- filter.is_zeroed(),
- "Forgot to zero the bloom filter last time"
- );
- StyleBloom {
- filter,
- elements: Default::default(),
- pushed_hashes: Default::default(),
- }
- }
-
- /// Return the bloom filter used properly by the `selectors` crate.
- pub fn filter(&self) -> &BloomFilter {
- &*self.filter
- }
-
- /// Push an element to the bloom filter, knowing that it's a child of the
- /// last element parent.
- pub fn push(&mut self, element: E) {
- if cfg!(debug_assertions) {
- if self.elements.is_empty() {
- assert!(element.traversal_parent().is_none());
- }
- }
- self.push_internal(element);
- }
-
- /// Same as `push`, but without asserting, in order to use it from
- /// `rebuild`.
- fn push_internal(&mut self, element: E) {
- let mut count = 0;
- each_relevant_element_hash(element, |hash| {
- count += 1;
- self.filter.insert_hash(hash);
- self.pushed_hashes.push(hash);
- });
- self.elements.push(PushedElement::new(element, count));
- }
-
- /// Pop the last element in the bloom filter and return it.
- #[inline]
- fn pop(&mut self) -> Option<E> {
- let PushedElement {
- element,
- num_hashes,
- } = self.elements.pop()?;
- let popped_element = *element;
-
- // Verify that the pushed hashes match the ones we'd get from the element.
- let mut expected_hashes = vec![];
- if cfg!(debug_assertions) {
- each_relevant_element_hash(popped_element, |hash| expected_hashes.push(hash));
- }
-
- for _ in 0..num_hashes {
- let hash = self.pushed_hashes.pop().unwrap();
- debug_assert_eq!(expected_hashes.pop().unwrap(), hash);
- self.filter.remove_hash(hash);
- }
-
- Some(popped_element)
- }
-
- /// Returns the DOM depth of elements that can be correctly
- /// matched against the bloom filter (that is, the number of
- /// elements in our list).
- pub fn matching_depth(&self) -> usize {
- self.elements.len()
- }
-
- /// Clears the bloom filter.
- pub fn clear(&mut self) {
- self.elements.clear();
-
- if self.pushed_hashes.len() > MEMSET_CLEAR_THRESHOLD {
- self.filter.clear();
- self.pushed_hashes.clear();
- } else {
- for hash in self.pushed_hashes.drain(..) {
- self.filter.remove_hash(hash);
- }
- debug_assert!(self.filter.is_zeroed());
- }
- }
-
- /// Rebuilds the bloom filter up to the parent of the given element.
- pub fn rebuild(&mut self, mut element: E) {
- self.clear();
-
- let mut parents_to_insert = SmallVec::<[E; 16]>::new();
- while let Some(parent) = element.traversal_parent() {
- parents_to_insert.push(parent);
- element = parent;
- }
-
- for parent in parents_to_insert.drain(..).rev() {
- self.push(parent);
- }
- }
-
- /// In debug builds, asserts that all the parents of `element` are in the
- /// bloom filter.
- ///
- /// Goes away in release builds.
- pub fn assert_complete(&self, mut element: E) {
- if cfg!(debug_assertions) {
- let mut checked = 0;
- while let Some(parent) = element.traversal_parent() {
- assert_eq!(
- parent,
- *(self.elements[self.elements.len() - 1 - checked].element)
- );
- element = parent;
- checked += 1;
- }
- assert_eq!(checked, self.elements.len());
- }
- }
-
- /// Get the element that represents the chain of things inserted
- /// into the filter right now. That chain is the given element
- /// (if any) and its ancestors.
- #[inline]
- pub fn current_parent(&self) -> Option<E> {
- self.elements.last().map(|ref el| *el.element)
- }
-
- /// Insert the parents of an element in the bloom filter, trying to recover
- /// the filter if the last element inserted doesn't match.
- ///
- /// Gets the element depth in the dom, to make it efficient, or if not
- /// provided always rebuilds the filter from scratch.
- ///
- /// Returns the new bloom filter depth, that the traversal code is
- /// responsible to keep around if it wants to get an effective filter.
- pub fn insert_parents_recovering(&mut self, element: E, element_depth: usize) {
- // Easy case, we're in a different restyle, or we're empty.
- if self.elements.is_empty() {
- self.rebuild(element);
- return;
- }
-
- let traversal_parent = match element.traversal_parent() {
- Some(parent) => parent,
- None => {
- // Yay, another easy case.
- self.clear();
- return;
- },
- };
-
- if self.current_parent() == Some(traversal_parent) {
- // Ta da, cache hit, we're all done.
- return;
- }
-
- if element_depth == 0 {
- self.clear();
- return;
- }
-
- // We should've early exited above.
- debug_assert!(
- element_depth != 0,
- "We should have already cleared the bloom filter"
- );
- debug_assert!(!self.elements.is_empty(), "How! We should've just rebuilt!");
-
- // Now the fun begins: We have the depth of the dom and the depth of the
- // last element inserted in the filter, let's try to find a common
- // parent.
- //
- // The current depth, that is, the depth of the last element inserted in
- // the bloom filter, is the number of elements _minus one_, that is: if
- // there's one element, it must be the root -> depth zero.
- let mut current_depth = self.elements.len() - 1;
-
- // If the filter represents an element too deep in the dom, we need to
- // pop ancestors.
- while current_depth > element_depth - 1 {
- self.pop().expect("Emilio is bad at math");
- current_depth -= 1;
- }
-
- // Now let's try to find a common parent in the bloom filter chain,
- // starting with traversal_parent.
- let mut common_parent = traversal_parent;
- let mut common_parent_depth = element_depth - 1;
-
- // Let's collect the parents we are going to need to insert once we've
- // found the common one.
- let mut parents_to_insert = SmallVec::<[E; 16]>::new();
-
- // If the bloom filter still doesn't have enough elements, the common
- // parent is up in the dom.
- while common_parent_depth > current_depth {
- // TODO(emilio): Seems like we could insert parents here, then
- // reverse the slice.
- parents_to_insert.push(common_parent);
- common_parent = common_parent.traversal_parent().expect("We were lied to");
- common_parent_depth -= 1;
- }
-
- // Now the two depths are the same.
- debug_assert_eq!(common_parent_depth, current_depth);
-
- // Happy case: The parents match, we only need to push the ancestors
- // we've collected and we'll never enter in this loop.
- //
- // Not-so-happy case: Parent's don't match, so we need to keep going up
- // until we find a common ancestor.
- //
- // Gecko currently models native anonymous content that conceptually
- // hangs off the document (such as scrollbars) as a separate subtree
- // from the document root.
- //
- // Thus it's possible with Gecko that we do not find any common
- // ancestor.
- while *(self.elements.last().unwrap().element) != common_parent {
- parents_to_insert.push(common_parent);
- self.pop().unwrap();
- common_parent = match common_parent.traversal_parent() {
- Some(parent) => parent,
- None => {
- debug_assert!(self.elements.is_empty());
- if cfg!(feature = "gecko") {
- break;
- } else {
- panic!("should have found a common ancestor");
- }
- },
- }
- }
-
- // Now the parents match, so insert the stack of elements we have been
- // collecting so far.
- for parent in parents_to_insert.drain(..).rev() {
- self.push(parent);
- }
-
- debug_assert_eq!(self.elements.len(), element_depth);
-
- // We're done! Easy.
- }
-}
diff --git a/components/style/build.rs b/components/style/build.rs
deleted file mode 100644
index eacb9b3fc99..00000000000
--- a/components/style/build.rs
+++ /dev/null
@@ -1,87 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
-
-#[macro_use]
-extern crate lazy_static;
-
-use std::env;
-use std::path::Path;
-use std::process::{exit, Command};
-use walkdir::WalkDir;
-
-#[cfg(feature = "gecko")]
-mod build_gecko;
-
-#[cfg(not(feature = "gecko"))]
-mod build_gecko {
- pub fn generate() {}
-}
-
-lazy_static! {
- pub static ref PYTHON: String = env::var("PYTHON3").ok().unwrap_or_else(|| {
- let candidates = if cfg!(windows) {
- ["python.exe"]
- } else {
- ["python3"]
- };
- for &name in &candidates {
- if Command::new(name)
- .arg("--version")
- .output()
- .ok()
- .map_or(false, |out| out.status.success())
- {
- return name.to_owned();
- }
- }
- panic!(
- "Can't find python (tried {})! Try fixing PATH or setting the PYTHON3 env var",
- candidates.join(", ")
- )
- });
-}
-
-fn generate_properties(engine: &str) {
- for entry in WalkDir::new("properties") {
- let entry = entry.unwrap();
- match entry.path().extension().and_then(|e| e.to_str()) {
- Some("mako") | Some("rs") | Some("py") | Some("zip") => {
- println!("cargo:rerun-if-changed={}", entry.path().display());
- },
- _ => {},
- }
- }
-
- let script = Path::new(&env::var_os("CARGO_MANIFEST_DIR").unwrap())
- .join("properties")
- .join("build.py");
-
- let status = Command::new(&*PYTHON)
- .arg(&script)
- .arg(engine)
- .arg("style-crate")
- .status()
- .unwrap();
- if !status.success() {
- exit(1)
- }
-}
-
-fn main() {
- let gecko = cfg!(feature = "gecko");
- let servo = cfg!(feature = "servo");
- let engine = match (gecko, servo) {
- (true, false) => "gecko",
- (false, true) => "servo",
- _ => panic!(
- "\n\n\
- The style crate requires enabling one of its 'servo' or 'gecko' feature flags. \
- \n\n"
- ),
- };
- println!("cargo:rerun-if-changed=build.rs");
- println!("cargo:out_dir={}", env::var("OUT_DIR").unwrap());
- generate_properties(engine);
- build_gecko::generate();
-}
diff --git a/components/style/build_gecko.rs b/components/style/build_gecko.rs
deleted file mode 100644
index a83c5dbc6d6..00000000000
--- a/components/style/build_gecko.rs
+++ /dev/null
@@ -1,400 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
-
-use super::PYTHON;
-use bindgen::{Builder, CodegenConfig};
-use regex::Regex;
-use std::cmp;
-use std::collections::HashSet;
-use std::env;
-use std::fs::{self, File};
-use std::io::{Read, Write};
-use std::path::{Path, PathBuf};
-use std::process::{exit, Command};
-use std::slice;
-use std::sync::Mutex;
-use std::time::SystemTime;
-use toml;
-use toml::value::Table;
-
-lazy_static! {
- static ref OUTDIR_PATH: PathBuf = PathBuf::from(env::var_os("OUT_DIR").unwrap()).join("gecko");
-}
-
-const STRUCTS_FILE: &'static str = "structs.rs";
-
-fn read_config(path: &PathBuf) -> Table {
- println!("cargo:rerun-if-changed={}", path.to_str().unwrap());
- update_last_modified(&path);
-
- let mut contents = String::new();
- File::open(path)
- .expect("Failed to open config file")
- .read_to_string(&mut contents)
- .expect("Failed to read config file");
- match toml::from_str::<Table>(&contents) {
- Ok(result) => result,
- Err(e) => panic!("Failed to parse config file: {}", e),
- }
-}
-
-lazy_static! {
- static ref CONFIG: Table = {
- // Load Gecko's binding generator config from the source tree.
- let path = mozbuild::TOPSRCDIR.join("layout/style/ServoBindings.toml");
- read_config(&path)
- };
- static ref BINDGEN_FLAGS: Vec<String> = {
- // Load build-specific config overrides.
- let path = mozbuild::TOPOBJDIR.join("layout/style/extra-bindgen-flags");
- println!("cargo:rerun-if-changed={}", path.to_str().unwrap());
- fs::read_to_string(path).expect("Failed to read extra-bindgen-flags file")
- .split_whitespace()
- .map(std::borrow::ToOwned::to_owned)
- .collect()
- };
- static ref INCLUDE_RE: Regex = Regex::new(r#"#include\s*"(.+?)""#).unwrap();
- static ref DISTDIR_PATH: PathBuf = mozbuild::TOPOBJDIR.join("dist");
- static ref SEARCH_PATHS: Vec<PathBuf> = vec![
- DISTDIR_PATH.join("include"),
- DISTDIR_PATH.join("include/nspr"),
- ];
- static ref ADDED_PATHS: Mutex<HashSet<PathBuf>> = Mutex::new(HashSet::new());
- static ref LAST_MODIFIED: Mutex<SystemTime> =
- Mutex::new(get_modified_time(&env::current_exe().unwrap())
- .expect("Failed to get modified time of executable"));
-}
-
-fn get_modified_time(file: &Path) -> Option<SystemTime> {
- file.metadata().and_then(|m| m.modified()).ok()
-}
-
-fn update_last_modified(file: &Path) {
- let modified = get_modified_time(file).expect("Couldn't get file modification time");
- let mut last_modified = LAST_MODIFIED.lock().unwrap();
- *last_modified = cmp::max(modified, *last_modified);
-}
-
-fn search_include(name: &str) -> Option<PathBuf> {
- for path in SEARCH_PATHS.iter() {
- let file = path.join(name);
- if file.is_file() {
- update_last_modified(&file);
- return Some(file);
- }
- }
- None
-}
-
-fn add_headers_recursively(path: PathBuf, added_paths: &mut HashSet<PathBuf>) {
- if added_paths.contains(&path) {
- return;
- }
- let mut file = File::open(&path).unwrap();
- let mut content = String::new();
- file.read_to_string(&mut content).unwrap();
- added_paths.insert(path);
- // Find all includes and add them recursively
- for cap in INCLUDE_RE.captures_iter(&content) {
- if let Some(path) = search_include(cap.get(1).unwrap().as_str()) {
- add_headers_recursively(path, added_paths);
- }
- }
-}
-
-fn add_include(name: &str) -> String {
- let mut added_paths = ADDED_PATHS.lock().unwrap();
- let file = match search_include(name) {
- Some(file) => file,
- None => panic!("Include not found: {}", name),
- };
- let result = String::from(file.to_str().unwrap());
- add_headers_recursively(file, &mut *added_paths);
- result
-}
-
-trait BuilderExt {
- fn get_initial_builder() -> Builder;
- fn include<T: Into<String>>(self, file: T) -> Builder;
-}
-
-impl BuilderExt for Builder {
- fn get_initial_builder() -> Builder {
- // Disable rust unions, because we replace some types inside of
- // them.
- let mut builder = Builder::default()
- .size_t_is_usize(true)
- .disable_untagged_union();
-
- let rustfmt_path = env::var_os("RUSTFMT")
- // This can be replaced with
- // > .filter(|p| !p.is_empty()).map(PathBuf::from)
- // once we can use 1.27+.
- .and_then(|p| {
- if p.is_empty() {
- None
- } else {
- Some(PathBuf::from(p))
- }
- });
- if let Some(path) = rustfmt_path {
- builder = builder.with_rustfmt(path);
- }
-
- for dir in SEARCH_PATHS.iter() {
- builder = builder.clang_arg("-I").clang_arg(dir.to_str().unwrap());
- }
-
- builder = builder.include(add_include("mozilla-config.h"));
-
- if env::var("CARGO_FEATURE_GECKO_DEBUG").is_ok() {
- builder = builder.clang_arg("-DDEBUG=1").clang_arg("-DJS_DEBUG=1");
- }
-
- for item in &*BINDGEN_FLAGS {
- builder = builder.clang_arg(item);
- }
-
- builder
- }
- fn include<T: Into<String>>(self, file: T) -> Builder {
- self.clang_arg("-include").clang_arg(file)
- }
-}
-
-struct Fixup {
- pat: String,
- rep: String,
-}
-
-fn write_binding_file(builder: Builder, file: &str, fixups: &[Fixup]) {
- let out_file = OUTDIR_PATH.join(file);
- if let Some(modified) = get_modified_time(&out_file) {
- // Don't generate the file if nothing it depends on was modified.
- let last_modified = LAST_MODIFIED.lock().unwrap();
- if *last_modified <= modified {
- return;
- }
- }
- let command_line_opts = builder.command_line_flags();
- let result = builder.generate();
- let mut result = match result {
- Ok(bindings) => bindings.to_string(),
- Err(_) => {
- panic!(
- "Failed to generate bindings, flags: {:?}",
- command_line_opts
- );
- },
- };
-
- for fixup in fixups.iter() {
- result = Regex::new(&fixup.pat)
- .unwrap()
- .replace_all(&result, &*fixup.rep)
- .into_owned()
- .into();
- }
- let bytes = result.into_bytes();
- File::create(&out_file)
- .unwrap()
- .write_all(&bytes)
- .expect("Unable to write output");
-}
-
-struct BuilderWithConfig<'a> {
- builder: Builder,
- config: &'a Table,
- used_keys: HashSet<&'static str>,
-}
-impl<'a> BuilderWithConfig<'a> {
- fn new(builder: Builder, config: &'a Table) -> Self {
- BuilderWithConfig {
- builder,
- config,
- used_keys: HashSet::new(),
- }
- }
-
- fn handle_list<F>(self, key: &'static str, func: F) -> BuilderWithConfig<'a>
- where
- F: FnOnce(Builder, slice::Iter<'a, toml::Value>) -> Builder,
- {
- let mut builder = self.builder;
- let config = self.config;
- let mut used_keys = self.used_keys;
- if let Some(list) = config.get(key) {
- used_keys.insert(key);
- builder = func(builder, list.as_array().unwrap().as_slice().iter());
- }
- BuilderWithConfig {
- builder,
- config,
- used_keys,
- }
- }
- fn handle_items<F>(self, key: &'static str, mut func: F) -> BuilderWithConfig<'a>
- where
- F: FnMut(Builder, &'a toml::Value) -> Builder,
- {
- self.handle_list(key, |b, iter| iter.fold(b, |b, item| func(b, item)))
- }
- fn handle_str_items<F>(self, key: &'static str, mut func: F) -> BuilderWithConfig<'a>
- where
- F: FnMut(Builder, &'a str) -> Builder,
- {
- self.handle_items(key, |b, item| func(b, item.as_str().unwrap()))
- }
- fn handle_table_items<F>(self, key: &'static str, mut func: F) -> BuilderWithConfig<'a>
- where
- F: FnMut(Builder, &'a Table) -> Builder,
- {
- self.handle_items(key, |b, item| func(b, item.as_table().unwrap()))
- }
- fn handle_common(self, fixups: &mut Vec<Fixup>) -> BuilderWithConfig<'a> {
- self.handle_str_items("headers", |b, item| b.header(add_include(item)))
- .handle_str_items("raw-lines", |b, item| b.raw_line(item))
- .handle_str_items("hide-types", |b, item| b.blocklist_type(item))
- .handle_table_items("fixups", |builder, item| {
- fixups.push(Fixup {
- pat: item["pat"].as_str().unwrap().into(),
- rep: item["rep"].as_str().unwrap().into(),
- });
- builder
- })
- }
-
- fn get_builder(self) -> Builder {
- for key in self.config.keys() {
- if !self.used_keys.contains(key.as_str()) {
- panic!("Unknown key: {}", key);
- }
- }
- self.builder
- }
-}
-
-fn generate_structs() {
- let builder = Builder::get_initial_builder()
- .enable_cxx_namespaces()
- .with_codegen_config(CodegenConfig::TYPES | CodegenConfig::VARS | CodegenConfig::FUNCTIONS);
- let mut fixups = vec![];
- let builder = BuilderWithConfig::new(builder, CONFIG["structs"].as_table().unwrap())
- .handle_common(&mut fixups)
- .handle_str_items("allowlist-functions", |b, item| b.allowlist_function(item))
- .handle_str_items("bitfield-enums", |b, item| b.bitfield_enum(item))
- .handle_str_items("rusty-enums", |b, item| b.rustified_enum(item))
- .handle_str_items("allowlist-vars", |b, item| b.allowlist_var(item))
- .handle_str_items("allowlist-types", |b, item| b.allowlist_type(item))
- .handle_str_items("opaque-types", |b, item| b.opaque_type(item))
- .handle_table_items("cbindgen-types", |b, item| {
- let gecko = item["gecko"].as_str().unwrap();
- let servo = item["servo"].as_str().unwrap();
- b.blocklist_type(format!("mozilla::{}", gecko))
- .module_raw_line("root::mozilla", format!("pub use {} as {};", servo, gecko))
- })
- .handle_table_items("mapped-generic-types", |builder, item| {
- let generic = item["generic"].as_bool().unwrap();
- let gecko = item["gecko"].as_str().unwrap();
- let servo = item["servo"].as_str().unwrap();
- let gecko_name = gecko.rsplit("::").next().unwrap();
- let gecko = gecko
- .split("::")
- .map(|s| format!("\\s*{}\\s*", s))
- .collect::<Vec<_>>()
- .join("::");
-
- fixups.push(Fixup {
- pat: format!("\\broot\\s*::\\s*{}\\b", gecko),
- rep: format!("crate::gecko_bindings::structs::{}", gecko_name),
- });
- builder.blocklist_type(gecko).raw_line(format!(
- "pub type {0}{2} = {1}{2};",
- gecko_name,
- servo,
- if generic { "<T>" } else { "" }
- ))
- })
- .get_builder();
- write_binding_file(builder, STRUCTS_FILE, &fixups);
-}
-
-fn setup_logging() -> bool {
- struct BuildLogger {
- file: Option<Mutex<fs::File>>,
- filter: String,
- }
-
- impl log::Log for BuildLogger {
- fn enabled(&self, meta: &log::Metadata) -> bool {
- self.file.is_some() && meta.target().contains(&self.filter)
- }
-
- fn log(&self, record: &log::Record) {
- if !self.enabled(record.metadata()) {
- return;
- }
-
- let mut file = self.file.as_ref().unwrap().lock().unwrap();
- let _ = writeln!(
- file,
- "{} - {} - {} @ {}:{}",
- record.level(),
- record.target(),
- record.args(),
- record.file().unwrap_or("<unknown>"),
- record.line().unwrap_or(0)
- );
- }
-
- fn flush(&self) {
- if let Some(ref file) = self.file {
- file.lock().unwrap().flush().unwrap();
- }
- }
- }
-
- if let Some(path) = env::var_os("STYLO_BUILD_LOG") {
- log::set_max_level(log::LevelFilter::Debug);
- log::set_boxed_logger(Box::new(BuildLogger {
- file: fs::File::create(path).ok().map(Mutex::new),
- filter: env::var("STYLO_BUILD_FILTER")
- .ok()
- .unwrap_or_else(|| "bindgen".to_owned()),
- }))
- .expect("Failed to set logger.");
-
- true
- } else {
- false
- }
-}
-
-fn generate_atoms() {
- let script = PathBuf::from(env::var_os("CARGO_MANIFEST_DIR").unwrap())
- .join("gecko")
- .join("regen_atoms.py");
- println!("cargo:rerun-if-changed={}", script.display());
- let status = Command::new(&*PYTHON)
- .arg(&script)
- .arg(DISTDIR_PATH.as_os_str())
- .arg(OUTDIR_PATH.as_os_str())
- .status()
- .unwrap();
- if !status.success() {
- exit(1);
- }
-}
-
-pub fn generate() {
- println!("cargo:rerun-if-changed=build_gecko.rs");
- fs::create_dir_all(&*OUTDIR_PATH).unwrap();
- setup_logging();
- generate_structs();
- generate_atoms();
-
- for path in ADDED_PATHS.lock().unwrap().iter() {
- println!("cargo:rerun-if-changed={}", path.to_str().unwrap());
- }
-}
diff --git a/components/style/color/convert.rs b/components/style/color/convert.rs
deleted file mode 100644
index 4fa037f9d6d..00000000000
--- a/components/style/color/convert.rs
+++ /dev/null
@@ -1,888 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
-
-//! Color conversion algorithms.
-//!
-//! Algorithms, matrices and constants are from the [color-4] specification,
-//! unless otherwise specified:
-//!
-//! https://drafts.csswg.org/css-color-4/#color-conversion-code
-//!
-//! NOTE: Matrices has to be transposed from the examples in the spec for use
-//! with the `euclid` library.
-
-use crate::color::ColorComponents;
-use std::f32::consts::PI;
-
-type Transform = euclid::default::Transform3D<f32>;
-type Vector = euclid::default::Vector3D<f32>;
-
-const RAD_PER_DEG: f32 = PI / 180.0;
-const DEG_PER_RAD: f32 = 180.0 / PI;
-
-/// Normalize hue into [0, 360).
-#[inline]
-fn normalize_hue(hue: f32) -> f32 {
- hue - 360. * (hue / 360.).floor()
-}
-
-/// Calculate the hue from RGB components and return it along with the min and
-/// max RGB values.
-#[inline]
-fn rgb_to_hue_min_max(red: f32, green: f32, blue: f32) -> (f32, f32, f32) {
- let max = red.max(green).max(blue);
- let min = red.min(green).min(blue);
-
- let delta = max - min;
-
- let hue = if delta != 0.0 {
- 60.0 * if max == red {
- (green - blue) / delta + if green < blue { 6.0 } else { 0.0 }
- } else if max == green {
- (blue - red) / delta + 2.0
- } else {
- (red - green) / delta + 4.0
- }
- } else {
- f32::NAN
- };
-
- (hue, min, max)
-}
-
-/// Convert a hue value into red, green, blue components.
-#[inline]
-fn hue_to_rgb(t1: f32, t2: f32, hue: f32) -> f32 {
- let hue = normalize_hue(hue);
-
- if hue * 6.0 < 360.0 {
- t1 + (t2 - t1) * hue / 60.0
- } else if hue * 2.0 < 360.0 {
- t2
- } else if hue * 3.0 < 720.0 {
- t1 + (t2 - t1) * (240.0 - hue) / 60.0
- } else {
- t1
- }
-}
-
-/// Convert from HSL notation to RGB notation.
-/// https://drafts.csswg.org/css-color-4/#hsl-to-rgb
-#[inline]
-pub fn hsl_to_rgb(from: &ColorComponents) -> ColorComponents {
- let ColorComponents(hue, saturation, lightness) = *from;
-
- let t2 = if lightness <= 0.5 {
- lightness * (saturation + 1.0)
- } else {
- lightness + saturation - lightness * saturation
- };
- let t1 = lightness * 2.0 - t2;
-
- ColorComponents(
- hue_to_rgb(t1, t2, hue + 120.0),
- hue_to_rgb(t1, t2, hue),
- hue_to_rgb(t1, t2, hue - 120.0),
- )
-}
-
-/// Convert from RGB notation to HSL notation.
-/// https://drafts.csswg.org/css-color-4/#rgb-to-hsl
-pub fn rgb_to_hsl(from: &ColorComponents) -> ColorComponents {
- let ColorComponents(red, green, blue) = *from;
-
- let (hue, min, max) = rgb_to_hue_min_max(red, green, blue);
-
- let lightness = (min + max) / 2.0;
- let delta = max - min;
-
- let saturation = if delta != 0.0 {
- if lightness == 0.0 || lightness == 1.0 {
- 0.0
- } else {
- (max - lightness) / lightness.min(1.0 - lightness)
- }
- } else {
- 0.0
- };
-
- ColorComponents(hue, saturation, lightness)
-}
-
-/// Convert from HWB notation to RGB notation.
-/// https://drafts.csswg.org/css-color-4/#hwb-to-rgb
-#[inline]
-pub fn hwb_to_rgb(from: &ColorComponents) -> ColorComponents {
- let ColorComponents(hue, whiteness, blackness) = *from;
-
- if whiteness + blackness > 1.0 {
- let gray = whiteness / (whiteness + blackness);
- return ColorComponents(gray, gray, gray);
- }
-
- let x = 1.0 - whiteness - blackness;
- hsl_to_rgb(&ColorComponents(hue, 1.0, 0.5)).map(|v| v * x + whiteness)
-}
-
-/// Convert from RGB notation to HWB notation.
-/// https://drafts.csswg.org/css-color-4/#rgb-to-hwb
-#[inline]
-pub fn rgb_to_hwb(from: &ColorComponents) -> ColorComponents {
- let ColorComponents(red, green, blue) = *from;
-
- let (hue, min, max) = rgb_to_hue_min_max(red, green, blue);
-
- let whiteness = min;
- let blackness = 1.0 - max;
-
- ColorComponents(hue, whiteness, blackness)
-}
-
-/// Convert from Lab to Lch. This calculation works for both Lab and Olab.
-/// <https://drafts.csswg.org/css-color-4/#color-conversion-code>
-#[inline]
-pub fn lab_to_lch(from: &ColorComponents) -> ColorComponents {
- let ColorComponents(lightness, a, b) = *from;
-
- let hue = normalize_hue(b.atan2(a) * 180.0 / PI);
- let chroma = (a.powf(2.0) + b.powf(2.0)).sqrt();
-
- ColorComponents(lightness, chroma, hue)
-}
-
-/// Convert from Lch to Lab. This calculation works for both Lch and Oklch.
-/// <https://drafts.csswg.org/css-color-4/#color-conversion-code>
-#[inline]
-pub fn lch_to_lab(from: &ColorComponents) -> ColorComponents {
- let ColorComponents(lightness, chroma, hue) = *from;
-
- let a = chroma * (hue * PI / 180.0).cos();
- let b = chroma * (hue * PI / 180.0).sin();
-
- ColorComponents(lightness, a, b)
-}
-
-#[inline]
-fn transform(from: &ColorComponents, mat: &Transform) -> ColorComponents {
- let result = mat.transform_vector3d(Vector::new(from.0, from.1, from.2));
- ColorComponents(result.x, result.y, result.z)
-}
-
-fn xyz_d65_to_xyz_d50(from: &ColorComponents) -> ColorComponents {
- #[rustfmt::skip]
- const MAT: Transform = Transform::new(
- 1.0479298208405488, 0.029627815688159344, -0.009243058152591178, 0.0,
- 0.022946793341019088, 0.990434484573249, 0.015055144896577895, 0.0,
- -0.05019222954313557, -0.01707382502938514, 0.7518742899580008, 0.0,
- 0.0, 0.0, 0.0, 1.0,
- );
-
- transform(from, &MAT)
-}
-
-fn xyz_d50_to_xyz_d65(from: &ColorComponents) -> ColorComponents {
- #[rustfmt::skip]
- const MAT: Transform = Transform::new(
- 0.9554734527042182, -0.028369706963208136, 0.012314001688319899, 0.0,
- -0.023098536874261423, 1.0099954580058226, -0.020507696433477912, 0.0,
- 0.0632593086610217, 0.021041398966943008, 1.3303659366080753, 0.0,
- 0.0, 0.0, 0.0, 1.0,
- );
-
- transform(from, &MAT)
-}
-
-/// A reference white that is used during color conversion.
-pub enum WhitePoint {
- /// D50 white reference.
- D50,
- /// D65 white reference.
- D65,
-}
-
-fn convert_white_point(from: WhitePoint, to: WhitePoint, components: &mut ColorComponents) {
- match (from, to) {
- (WhitePoint::D50, WhitePoint::D65) => *components = xyz_d50_to_xyz_d65(components),
- (WhitePoint::D65, WhitePoint::D50) => *components = xyz_d65_to_xyz_d50(components),
-
- _ => {},
- }
-}
-
-/// A trait that allows conversion of color spaces to and from XYZ coordinate
-/// space with a specified white point.
-///
-/// Allows following the specified method of converting between color spaces:
-/// - Convert to values to sRGB linear light.
-/// - Convert to XYZ coordinate space.
-/// - Adjust white point to target white point.
-/// - Convert to sRGB linear light in target color space.
-/// - Convert to sRGB gamma encoded in target color space.
-///
-/// https://drafts.csswg.org/css-color-4/#color-conversion
-pub trait ColorSpaceConversion {
- /// The white point that the implementer is represented in.
- const WHITE_POINT: WhitePoint;
-
- /// Convert the components from sRGB gamma encoded values to sRGB linear
- /// light values.
- fn to_linear_light(from: &ColorComponents) -> ColorComponents;
-
- /// Convert the components from sRGB linear light values to XYZ coordinate
- /// space.
- fn to_xyz(from: &ColorComponents) -> ColorComponents;
-
- /// Convert the components from XYZ coordinate space to sRGB linear light
- /// values.
- fn from_xyz(from: &ColorComponents) -> ColorComponents;
-
- /// Convert the components from sRGB linear light values to sRGB gamma
- /// encoded values.
- fn to_gamma_encoded(from: &ColorComponents) -> ColorComponents;
-}
-
-/// Convert the color components from the specified color space to XYZ and
-/// return the components and the white point they are in.
-pub fn to_xyz<From: ColorSpaceConversion>(from: &ColorComponents) -> (ColorComponents, WhitePoint) {
- // Convert the color components where in-gamut values are in the range
- // [0 - 1] to linear light (un-companded) form.
- let result = From::to_linear_light(from);
-
- // Convert the color components from the source color space to XYZ.
- (From::to_xyz(&result), From::WHITE_POINT)
-}
-
-/// Convert the color components from XYZ at the given white point to the
-/// specified color space.
-pub fn from_xyz<To: ColorSpaceConversion>(
- from: &ColorComponents,
- white_point: WhitePoint,
-) -> ColorComponents {
- let mut xyz = from.clone();
-
- // Convert the white point if needed.
- convert_white_point(white_point, To::WHITE_POINT, &mut xyz);
-
- // Convert the color from XYZ to the target color space.
- let result = To::from_xyz(&xyz);
-
- // Convert the color components of linear-light values in the range
- // [0 - 1] to a gamma corrected form.
- To::to_gamma_encoded(&result)
-}
-
-/// The sRGB color space.
-/// https://drafts.csswg.org/css-color-4/#predefined-sRGB
-pub struct Srgb;
-
-impl Srgb {
- #[rustfmt::skip]
- const TO_XYZ: Transform = Transform::new(
- 0.4123907992659595, 0.21263900587151036, 0.01933081871559185, 0.0,
- 0.35758433938387796, 0.7151686787677559, 0.11919477979462599, 0.0,
- 0.1804807884018343, 0.07219231536073371, 0.9505321522496606, 0.0,
- 0.0, 0.0, 0.0, 1.0,
- );
-
- #[rustfmt::skip]
- const FROM_XYZ: Transform = Transform::new(
- 3.2409699419045213, -0.9692436362808798, 0.05563007969699361, 0.0,
- -1.5373831775700935, 1.8759675015077206, -0.20397695888897657, 0.0,
- -0.4986107602930033, 0.04155505740717561, 1.0569715142428786, 0.0,
- 0.0, 0.0, 0.0, 1.0,
- );
-}
-
-impl ColorSpaceConversion for Srgb {
- const WHITE_POINT: WhitePoint = WhitePoint::D65;
-
- fn to_linear_light(from: &ColorComponents) -> ColorComponents {
- from.clone().map(|value| {
- let abs = value.abs();
-
- if abs < 0.04045 {
- value / 12.92
- } else {
- value.signum() * ((abs + 0.055) / 1.055).powf(2.4)
- }
- })
- }
-
- fn to_xyz(from: &ColorComponents) -> ColorComponents {
- transform(from, &Self::TO_XYZ)
- }
-
- fn from_xyz(from: &ColorComponents) -> ColorComponents {
- transform(from, &Self::FROM_XYZ)
- }
-
- fn to_gamma_encoded(from: &ColorComponents) -> ColorComponents {
- from.clone().map(|value| {
- let abs = value.abs();
-
- if abs > 0.0031308 {
- value.signum() * (1.055 * abs.powf(1.0 / 2.4) - 0.055)
- } else {
- 12.92 * value
- }
- })
- }
-}
-
-/// Color specified with hue, saturation and lightness components.
-pub struct Hsl;
-
-impl ColorSpaceConversion for Hsl {
- const WHITE_POINT: WhitePoint = Srgb::WHITE_POINT;
-
- fn to_linear_light(from: &ColorComponents) -> ColorComponents {
- Srgb::to_linear_light(&hsl_to_rgb(from))
- }
-
- #[inline]
- fn to_xyz(from: &ColorComponents) -> ColorComponents {
- Srgb::to_xyz(from)
- }
-
- #[inline]
- fn from_xyz(from: &ColorComponents) -> ColorComponents {
- Srgb::from_xyz(from)
- }
-
- fn to_gamma_encoded(from: &ColorComponents) -> ColorComponents {
- rgb_to_hsl(&Srgb::to_gamma_encoded(from))
- }
-}
-
-/// Color specified with hue, whiteness and blackness components.
-pub struct Hwb;
-
-impl ColorSpaceConversion for Hwb {
- const WHITE_POINT: WhitePoint = Srgb::WHITE_POINT;
-
- fn to_linear_light(from: &ColorComponents) -> ColorComponents {
- Srgb::to_linear_light(&hwb_to_rgb(from))
- }
-
- #[inline]
- fn to_xyz(from: &ColorComponents) -> ColorComponents {
- Srgb::to_xyz(from)
- }
-
- #[inline]
- fn from_xyz(from: &ColorComponents) -> ColorComponents {
- Srgb::from_xyz(from)
- }
-
- fn to_gamma_encoded(from: &ColorComponents) -> ColorComponents {
- rgb_to_hwb(&Srgb::to_gamma_encoded(from))
- }
-}
-
-/// The same as sRGB color space, except the transfer function is linear light.
-/// https://drafts.csswg.org/css-color-4/#predefined-sRGB-linear
-pub struct SrgbLinear;
-
-impl ColorSpaceConversion for SrgbLinear {
- const WHITE_POINT: WhitePoint = Srgb::WHITE_POINT;
-
- fn to_linear_light(from: &ColorComponents) -> ColorComponents {
- // Already in linear light form.
- from.clone()
- }
-
- fn to_xyz(from: &ColorComponents) -> ColorComponents {
- Srgb::to_xyz(from)
- }
-
- fn from_xyz(from: &ColorComponents) -> ColorComponents {
- Srgb::from_xyz(from)
- }
-
- fn to_gamma_encoded(from: &ColorComponents) -> ColorComponents {
- // Stay in linear light form.
- from.clone()
- }
-}
-
-/// The Display-P3 color space.
-/// https://drafts.csswg.org/css-color-4/#predefined-display-p3
-pub struct DisplayP3;
-
-impl DisplayP3 {
- #[rustfmt::skip]
- const TO_XYZ: Transform = Transform::new(
- 0.48657094864821626, 0.22897456406974884, 0.0, 0.0,
- 0.26566769316909294, 0.6917385218365062, 0.045113381858902575, 0.0,
- 0.1982172852343625, 0.079286914093745, 1.0439443689009757, 0.0,
- 0.0, 0.0, 0.0, 1.0,
- );
-
- #[rustfmt::skip]
- const FROM_XYZ: Transform = Transform::new(
- 2.4934969119414245, -0.829488969561575, 0.035845830243784335, 0.0,
- -0.9313836179191236, 1.7626640603183468, -0.07617238926804171, 0.0,
- -0.40271078445071684, 0.02362468584194359, 0.9568845240076873, 0.0,
- 0.0, 0.0, 0.0, 1.0,
- );
-}
-
-impl ColorSpaceConversion for DisplayP3 {
- const WHITE_POINT: WhitePoint = WhitePoint::D65;
-
- fn to_linear_light(from: &ColorComponents) -> ColorComponents {
- Srgb::to_linear_light(from)
- }
-
- fn to_xyz(from: &ColorComponents) -> ColorComponents {
- transform(from, &Self::TO_XYZ)
- }
-
- fn from_xyz(from: &ColorComponents) -> ColorComponents {
- transform(from, &Self::FROM_XYZ)
- }
-
- fn to_gamma_encoded(from: &ColorComponents) -> ColorComponents {
- Srgb::to_gamma_encoded(from)
- }
-}
-
-/// The a98-rgb color space.
-/// https://drafts.csswg.org/css-color-4/#predefined-a98-rgb
-pub struct A98Rgb;
-
-impl A98Rgb {
- #[rustfmt::skip]
- const TO_XYZ: Transform = Transform::new(
- 0.5766690429101308, 0.29734497525053616, 0.027031361386412378, 0.0,
- 0.18555823790654627, 0.627363566255466, 0.07068885253582714, 0.0,
- 0.18822864623499472, 0.07529145849399789, 0.9913375368376389, 0.0,
- 0.0, 0.0, 0.0, 1.0,
- );
-
- #[rustfmt::skip]
- const FROM_XYZ: Transform = Transform::new(
- 2.041587903810746, -0.9692436362808798, 0.013444280632031024, 0.0,
- -0.5650069742788596, 1.8759675015077206, -0.11836239223101824, 0.0,
- -0.3447313507783295, 0.04155505740717561, 1.0151749943912054, 0.0,
- 0.0, 0.0, 0.0, 1.0,
- );
-}
-
-impl ColorSpaceConversion for A98Rgb {
- const WHITE_POINT: WhitePoint = WhitePoint::D65;
-
- fn to_linear_light(from: &ColorComponents) -> ColorComponents {
- from.clone().map(|v| v.signum() * v.abs().powf(2.19921875))
- }
-
- fn to_xyz(from: &ColorComponents) -> ColorComponents {
- transform(from, &Self::TO_XYZ)
- }
-
- fn from_xyz(from: &ColorComponents) -> ColorComponents {
- transform(from, &Self::FROM_XYZ)
- }
-
- fn to_gamma_encoded(from: &ColorComponents) -> ColorComponents {
- from.clone()
- .map(|v| v.signum() * v.abs().powf(0.4547069271758437))
- }
-}
-
-/// The ProPhoto RGB color space.
-/// https://drafts.csswg.org/css-color-4/#predefined-prophoto-rgb
-pub struct ProphotoRgb;
-
-impl ProphotoRgb {
- #[rustfmt::skip]
- const TO_XYZ: Transform = Transform::new(
- 0.7977604896723027, 0.2880711282292934, 0.0, 0.0,
- 0.13518583717574031, 0.7118432178101014, 0.0, 0.0,
- 0.0313493495815248, 0.00008565396060525902, 0.8251046025104601, 0.0,
- 0.0, 0.0, 0.0, 1.0,
- );
-
- #[rustfmt::skip]
- const FROM_XYZ: Transform = Transform::new(
- 1.3457989731028281, -0.5446224939028347, 0.0, 0.0,
- -0.25558010007997534, 1.5082327413132781, 0.0, 0.0,
- -0.05110628506753401, 0.02053603239147973, 1.2119675456389454, 0.0,
- 0.0, 0.0, 0.0, 1.0,
- );
-}
-
-impl ColorSpaceConversion for ProphotoRgb {
- const WHITE_POINT: WhitePoint = WhitePoint::D50;
-
- fn to_linear_light(from: &ColorComponents) -> ColorComponents {
- from.clone().map(|value| {
- const ET2: f32 = 16.0 / 512.0;
-
- let abs = value.abs();
-
- if abs <= ET2 {
- value / 16.0
- } else {
- value.signum() * abs.powf(1.8)
- }
- })
- }
-
- fn to_xyz(from: &ColorComponents) -> ColorComponents {
- transform(from, &Self::TO_XYZ)
- }
-
- fn from_xyz(from: &ColorComponents) -> ColorComponents {
- transform(from, &Self::FROM_XYZ)
- }
-
- fn to_gamma_encoded(from: &ColorComponents) -> ColorComponents {
- const ET: f32 = 1.0 / 512.0;
-
- from.clone().map(|v| {
- let abs = v.abs();
- if abs >= ET {
- v.signum() * abs.powf(1.0 / 1.8)
- } else {
- 16.0 * v
- }
- })
- }
-}
-
-/// The Rec.2020 color space.
-/// https://drafts.csswg.org/css-color-4/#predefined-rec2020
-pub struct Rec2020;
-
-impl Rec2020 {
- const ALPHA: f32 = 1.09929682680944;
- const BETA: f32 = 0.018053968510807;
-
- #[rustfmt::skip]
- const TO_XYZ: Transform = Transform::new(
- 0.6369580483012913, 0.26270021201126703, 0.0, 0.0,
- 0.14461690358620838, 0.677998071518871, 0.028072693049087508, 0.0,
- 0.16888097516417205, 0.059301716469861945, 1.0609850577107909, 0.0,
- 0.0, 0.0, 0.0, 1.0,
- );
-
- #[rustfmt::skip]
- const FROM_XYZ: Transform = Transform::new(
- 1.7166511879712676, -0.666684351832489, 0.017639857445310915, 0.0,
- -0.3556707837763924, 1.616481236634939, -0.042770613257808655, 0.0,
- -0.2533662813736598, 0.01576854581391113, 0.942103121235474, 0.0,
- 0.0, 0.0, 0.0, 1.0,
- );
-}
-
-impl ColorSpaceConversion for Rec2020 {
- const WHITE_POINT: WhitePoint = WhitePoint::D65;
-
- fn to_linear_light(from: &ColorComponents) -> ColorComponents {
- from.clone().map(|value| {
- let abs = value.abs();
-
- if abs < Self::BETA * 4.5 {
- value / 4.5
- } else {
- value.signum() * ((abs + Self::ALPHA - 1.0) / Self::ALPHA).powf(1.0 / 0.45)
- }
- })
- }
-
- fn to_xyz(from: &ColorComponents) -> ColorComponents {
- transform(from, &Self::TO_XYZ)
- }
-
- fn from_xyz(from: &ColorComponents) -> ColorComponents {
- transform(from, &Self::FROM_XYZ)
- }
-
- fn to_gamma_encoded(from: &ColorComponents) -> ColorComponents {
- from.clone().map(|v| {
- let abs = v.abs();
-
- if abs > Self::BETA {
- v.signum() * (Self::ALPHA * abs.powf(0.45) - (Self::ALPHA - 1.0))
- } else {
- 4.5 * v
- }
- })
- }
-}
-
-/// A color in the XYZ coordinate space with a D50 white reference.
-/// https://drafts.csswg.org/css-color-4/#predefined-xyz
-pub struct XyzD50;
-
-impl ColorSpaceConversion for XyzD50 {
- const WHITE_POINT: WhitePoint = WhitePoint::D50;
-
- fn to_linear_light(from: &ColorComponents) -> ColorComponents {
- from.clone()
- }
-
- fn to_xyz(from: &ColorComponents) -> ColorComponents {
- from.clone()
- }
-
- fn from_xyz(from: &ColorComponents) -> ColorComponents {
- from.clone()
- }
-
- fn to_gamma_encoded(from: &ColorComponents) -> ColorComponents {
- from.clone()
- }
-}
-
-/// A color in the XYZ coordinate space with a D65 white reference.
-/// https://drafts.csswg.org/css-color-4/#predefined-xyz
-pub struct XyzD65;
-
-impl ColorSpaceConversion for XyzD65 {
- const WHITE_POINT: WhitePoint = WhitePoint::D65;
-
- fn to_linear_light(from: &ColorComponents) -> ColorComponents {
- from.clone()
- }
-
- fn to_xyz(from: &ColorComponents) -> ColorComponents {
- from.clone()
- }
-
- fn from_xyz(from: &ColorComponents) -> ColorComponents {
- from.clone()
- }
-
- fn to_gamma_encoded(from: &ColorComponents) -> ColorComponents {
- from.clone()
- }
-}
-
-/// The Lab color space.
-/// https://drafts.csswg.org/css-color-4/#specifying-lab-lch
-pub struct Lab;
-
-impl Lab {
- const KAPPA: f32 = 24389.0 / 27.0;
- const EPSILON: f32 = 216.0 / 24389.0;
- const WHITE: ColorComponents = ColorComponents(0.96422, 1.0, 0.82521);
-}
-
-impl ColorSpaceConversion for Lab {
- const WHITE_POINT: WhitePoint = WhitePoint::D50;
-
- fn to_linear_light(from: &ColorComponents) -> ColorComponents {
- // No need for conversion.
- from.clone()
- }
-
- /// Convert a CIELAB color to XYZ as specified in [1] and [2].
- ///
- /// [1]: https://drafts.csswg.org/css-color/#lab-to-predefined
- /// [2]: https://drafts.csswg.org/css-color/#color-conversion-code
- fn to_xyz(from: &ColorComponents) -> ColorComponents {
- let f1 = (from.0 + 16.0) / 116.0;
- let f0 = (from.1 / 500.0) + f1;
- let f2 = f1 - from.2 / 200.0;
-
- let x = if f0.powf(3.0) > Self::EPSILON {
- f0.powf(3.)
- } else {
- (116.0 * f0 - 16.0) / Self::KAPPA
- };
- let y = if from.0 > Self::KAPPA * Self::EPSILON {
- ((from.0 + 16.0) / 116.0).powf(3.0)
- } else {
- from.0 / Self::KAPPA
- };
- let z = if f2.powf(3.0) > Self::EPSILON {
- f2.powf(3.0)
- } else {
- (116.0 * f2 - 16.0) / Self::KAPPA
- };
-
- ColorComponents(x * Self::WHITE.0, y * Self::WHITE.1, z * Self::WHITE.2)
- }
-
- /// Convert an XYZ colour to LAB as specified in [1] and [2].
- ///
- /// [1]: https://drafts.csswg.org/css-color/#rgb-to-lab
- /// [2]: https://drafts.csswg.org/css-color/#color-conversion-code
- fn from_xyz(from: &ColorComponents) -> ColorComponents {
- macro_rules! compute_f {
- ($value:expr) => {{
- if $value > Self::EPSILON {
- $value.cbrt()
- } else {
- (Self::KAPPA * $value + 16.0) / 116.0
- }
- }};
- }
-
- // 4. Convert D50-adapted XYZ to Lab.
- let f = [
- compute_f!(from.0 / Self::WHITE.0),
- compute_f!(from.1 / Self::WHITE.1),
- compute_f!(from.2 / Self::WHITE.2),
- ];
-
- let lightness = 116.0 * f[1] - 16.0;
- let a = 500.0 * (f[0] - f[1]);
- let b = 200.0 * (f[1] - f[2]);
-
- ColorComponents(lightness, a, b)
- }
-
- fn to_gamma_encoded(from: &ColorComponents) -> ColorComponents {
- // No need for conversion.
- from.clone()
- }
-}
-
-/// The Lch color space.
-/// https://drafts.csswg.org/css-color-4/#specifying-lab-lch
-pub struct Lch;
-
-impl ColorSpaceConversion for Lch {
- const WHITE_POINT: WhitePoint = Lab::WHITE_POINT;
-
- fn to_linear_light(from: &ColorComponents) -> ColorComponents {
- // No need for conversion.
- from.clone()
- }
-
- fn to_xyz(from: &ColorComponents) -> ColorComponents {
- // Convert LCH to Lab first.
- let hue = from.2 * RAD_PER_DEG;
- let a = from.1 * hue.cos();
- let b = from.1 * hue.sin();
-
- let lab = ColorComponents(from.0, a, b);
-
- // Then convert the Lab to XYZ.
- Lab::to_xyz(&lab)
- }
-
- fn from_xyz(from: &ColorComponents) -> ColorComponents {
- // First convert the XYZ to LAB.
- let ColorComponents(lightness, a, b) = Lab::from_xyz(&from);
-
- // Then conver the Lab to LCH.
- let hue = b.atan2(a) * DEG_PER_RAD;
- let chroma = (a * a + b * b).sqrt();
-
- ColorComponents(lightness, chroma, hue)
- }
-
- fn to_gamma_encoded(from: &ColorComponents) -> ColorComponents {
- // No need for conversion.
- from.clone()
- }
-}
-
-/// The Oklab color space.
-/// https://drafts.csswg.org/css-color-4/#specifying-oklab-oklch
-pub struct Oklab;
-
-impl Oklab {
- #[rustfmt::skip]
- const XYZ_TO_LMS: Transform = Transform::new(
- 0.8190224432164319, 0.0329836671980271, 0.048177199566046255, 0.0,
- 0.3619062562801221, 0.9292868468965546, 0.26423952494422764, 0.0,
- -0.12887378261216414, 0.03614466816999844, 0.6335478258136937, 0.0,
- 0.0, 0.0, 0.0, 1.0,
- );
-
- #[rustfmt::skip]
- const LMS_TO_OKLAB: Transform = Transform::new(
- 0.2104542553, 1.9779984951, 0.0259040371, 0.0,
- 0.7936177850, -2.4285922050, 0.7827717662, 0.0,
- -0.0040720468, 0.4505937099, -0.8086757660, 0.0,
- 0.0, 0.0, 0.0, 1.0,
- );
-
- #[rustfmt::skip]
- const LMS_TO_XYZ: Transform = Transform::new(
- 1.2268798733741557, -0.04057576262431372, -0.07637294974672142, 0.0,
- -0.5578149965554813, 1.1122868293970594, -0.4214933239627914, 0.0,
- 0.28139105017721583, -0.07171106666151701, 1.5869240244272418, 0.0,
- 0.0, 0.0, 0.0, 1.0,
- );
-
- #[rustfmt::skip]
- const OKLAB_TO_LMS: Transform = Transform::new(
- 0.99999999845051981432, 1.0000000088817607767, 1.0000000546724109177, 0.0,
- 0.39633779217376785678, -0.1055613423236563494, -0.089484182094965759684, 0.0,
- 0.21580375806075880339, -0.063854174771705903402, -1.2914855378640917399, 0.0,
- 0.0, 0.0, 0.0, 1.0,
- );
-}
-
-impl ColorSpaceConversion for Oklab {
- const WHITE_POINT: WhitePoint = WhitePoint::D65;
-
- fn to_linear_light(from: &ColorComponents) -> ColorComponents {
- // No need for conversion.
- from.clone()
- }
-
- fn to_xyz(from: &ColorComponents) -> ColorComponents {
- let lms = transform(&from, &Self::OKLAB_TO_LMS);
- let lms = lms.map(|v| v.powf(3.0));
- transform(&lms, &Self::LMS_TO_XYZ)
- }
-
- fn from_xyz(from: &ColorComponents) -> ColorComponents {
- let lms = transform(&from, &Self::XYZ_TO_LMS);
- let lms = lms.map(|v| v.cbrt());
- transform(&lms, &Self::LMS_TO_OKLAB)
- }
-
- fn to_gamma_encoded(from: &ColorComponents) -> ColorComponents {
- // No need for conversion.
- from.clone()
- }
-}
-
-/// The Oklch color space.
-/// https://drafts.csswg.org/css-color-4/#specifying-oklab-oklch
-pub struct Oklch;
-
-impl ColorSpaceConversion for Oklch {
- const WHITE_POINT: WhitePoint = Oklab::WHITE_POINT;
-
- fn to_linear_light(from: &ColorComponents) -> ColorComponents {
- // No need for conversion.
- from.clone()
- }
-
- fn to_xyz(from: &ColorComponents) -> ColorComponents {
- // First convert OkLCH to Oklab.
- let hue = from.2 * RAD_PER_DEG;
- let a = from.1 * hue.cos();
- let b = from.1 * hue.sin();
- let oklab = ColorComponents(from.0, a, b);
-
- // Then convert Oklab to XYZ.
- Oklab::to_xyz(&oklab)
- }
-
- fn from_xyz(from: &ColorComponents) -> ColorComponents {
- // First convert XYZ to Oklab.
- let ColorComponents(lightness, a, b) = Oklab::from_xyz(&from);
-
- // Then convert Oklab to OkLCH.
- let hue = b.atan2(a) * DEG_PER_RAD;
- let chroma = (a * a + b * b).sqrt();
-
- ColorComponents(lightness, chroma, hue)
- }
-
- fn to_gamma_encoded(from: &ColorComponents) -> ColorComponents {
- // No need for conversion.
- from.clone()
- }
-}
diff --git a/components/style/color/mix.rs b/components/style/color/mix.rs
deleted file mode 100644
index 455d0252659..00000000000
--- a/components/style/color/mix.rs
+++ /dev/null
@@ -1,475 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
-
-//! Color mixing/interpolation.
-
-use super::{AbsoluteColor, ColorComponents, ColorFlags, ColorSpace};
-use crate::parser::{Parse, ParserContext};
-use cssparser::Parser;
-use std::fmt::{self, Write};
-use style_traits::{CssWriter, ParseError, ToCss};
-
-/// A hue-interpolation-method as defined in [1].
-///
-/// [1]: https://drafts.csswg.org/css-color-4/#typedef-hue-interpolation-method
-#[derive(
- Clone,
- Copy,
- Debug,
- Eq,
- MallocSizeOf,
- Parse,
- PartialEq,
- ToAnimatedValue,
- ToComputedValue,
- ToCss,
- ToResolvedValue,
- ToShmem,
-)]
-#[repr(u8)]
-pub enum HueInterpolationMethod {
- /// https://drafts.csswg.org/css-color-4/#shorter
- Shorter,
- /// https://drafts.csswg.org/css-color-4/#longer
- Longer,
- /// https://drafts.csswg.org/css-color-4/#increasing
- Increasing,
- /// https://drafts.csswg.org/css-color-4/#decreasing
- Decreasing,
- /// https://drafts.csswg.org/css-color-4/#specified
- Specified,
-}
-
-/// https://drafts.csswg.org/css-color-4/#color-interpolation-method
-#[derive(
- Clone,
- Copy,
- Debug,
- Eq,
- MallocSizeOf,
- PartialEq,
- ToShmem,
- ToAnimatedValue,
- ToComputedValue,
- ToResolvedValue,
-)]
-#[repr(C)]
-pub struct ColorInterpolationMethod {
- /// The color-space the interpolation should be done in.
- pub space: ColorSpace,
- /// The hue interpolation method.
- pub hue: HueInterpolationMethod,
-}
-
-impl ColorInterpolationMethod {
- /// Returns the srgb interpolation method.
- pub const fn srgb() -> Self {
- Self {
- space: ColorSpace::Srgb,
- hue: HueInterpolationMethod::Shorter,
- }
- }
-
- /// Return the oklab interpolation method used for default color
- /// interpolcation.
- pub const fn oklab() -> Self {
- Self {
- space: ColorSpace::Oklab,
- hue: HueInterpolationMethod::Shorter,
- }
- }
-
- /// Decides the best method for interpolating between the given colors.
- /// https://drafts.csswg.org/css-color-4/#interpolation-space
- pub fn best_interpolation_between(left: &AbsoluteColor, right: &AbsoluteColor) -> Self {
- // The preferred color space to use for interpolating colors is Oklab.
- // However, if either of the colors are in legacy rgb(), hsl() or hwb(),
- // then interpolation is done in sRGB.
- if !left.is_legacy_color() || !right.is_legacy_color() {
- Self::oklab()
- } else {
- Self::srgb()
- }
- }
-}
-
-impl Parse for ColorInterpolationMethod {
- fn parse<'i, 't>(
- _: &ParserContext,
- input: &mut Parser<'i, 't>,
- ) -> Result<Self, ParseError<'i>> {
- input.expect_ident_matching("in")?;
- let space = ColorSpace::parse(input)?;
- // https://drafts.csswg.org/css-color-4/#hue-interpolation
- // Unless otherwise specified, if no specific hue interpolation
- // algorithm is selected by the host syntax, the default is shorter.
- let hue = if space.is_polar() {
- input
- .try_parse(|input| -> Result<_, ParseError<'i>> {
- let hue = HueInterpolationMethod::parse(input)?;
- input.expect_ident_matching("hue")?;
- Ok(hue)
- })
- .unwrap_or(HueInterpolationMethod::Shorter)
- } else {
- HueInterpolationMethod::Shorter
- };
- Ok(Self { space, hue })
- }
-}
-
-impl ToCss for ColorInterpolationMethod {
- fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
- where
- W: Write,
- {
- dest.write_str("in ")?;
- self.space.to_css(dest)?;
- if self.hue != HueInterpolationMethod::Shorter {
- dest.write_char(' ')?;
- self.hue.to_css(dest)?;
- dest.write_str(" hue")?;
- }
- Ok(())
- }
-}
-
-/// Mix two colors into one.
-pub fn mix(
- interpolation: ColorInterpolationMethod,
- left_color: &AbsoluteColor,
- mut left_weight: f32,
- right_color: &AbsoluteColor,
- mut right_weight: f32,
- normalize_weights: bool,
-) -> AbsoluteColor {
- // https://drafts.csswg.org/css-color-5/#color-mix-percent-norm
- let mut alpha_multiplier = 1.0;
- if normalize_weights {
- let sum = left_weight + right_weight;
- if sum != 1.0 {
- let scale = 1.0 / sum;
- left_weight *= scale;
- right_weight *= scale;
- if sum < 1.0 {
- alpha_multiplier = sum;
- }
- }
- }
-
- mix_in(
- interpolation.space,
- left_color,
- left_weight,
- right_color,
- right_weight,
- interpolation.hue,
- alpha_multiplier,
- )
-}
-
-/// What the outcome of each component should be in a mix result.
-#[derive(Clone, Copy)]
-#[repr(u8)]
-enum ComponentMixOutcome {
- /// Mix the left and right sides to give the result.
- Mix,
- /// Carry the left side forward to the result.
- UseLeft,
- /// Carry the right side forward to the result.
- UseRight,
- /// The resulting component should also be none.
- None,
-}
-
-impl ComponentMixOutcome {
- fn from_colors(
- left: &AbsoluteColor,
- right: &AbsoluteColor,
- flags_to_check: ColorFlags,
- ) -> Self {
- match (
- left.flags.contains(flags_to_check),
- right.flags.contains(flags_to_check),
- ) {
- (true, true) => Self::None,
- (true, false) => Self::UseRight,
- (false, true) => Self::UseLeft,
- (false, false) => Self::Mix,
- }
- }
-}
-
-fn mix_in(
- color_space: ColorSpace,
- left_color: &AbsoluteColor,
- left_weight: f32,
- right_color: &AbsoluteColor,
- right_weight: f32,
- hue_interpolation: HueInterpolationMethod,
- alpha_multiplier: f32,
-) -> AbsoluteColor {
- let outcomes = [
- ComponentMixOutcome::from_colors(left_color, right_color, ColorFlags::C1_IS_NONE),
- ComponentMixOutcome::from_colors(left_color, right_color, ColorFlags::C2_IS_NONE),
- ComponentMixOutcome::from_colors(left_color, right_color, ColorFlags::C3_IS_NONE),
- ComponentMixOutcome::from_colors(left_color, right_color, ColorFlags::ALPHA_IS_NONE),
- ];
-
- // Convert both colors into the interpolation color space.
- let left = left_color.to_color_space(color_space);
- let left = left.raw_components();
-
- let right = right_color.to_color_space(color_space);
- let right = right.raw_components();
-
- let (result, result_flags) = interpolate_premultiplied(
- &left,
- left_weight,
- &right,
- right_weight,
- color_space.hue_index(),
- hue_interpolation,
- &outcomes,
- );
-
- let alpha = if alpha_multiplier != 1.0 {
- result[3] * alpha_multiplier
- } else {
- result[3]
- };
-
- // FIXME: In rare cases we end up with 0.999995 in the alpha channel,
- // so we reduce the precision to avoid serializing to
- // rgba(?, ?, ?, 1). This is not ideal, so we should look into
- // ways to avoid it. Maybe pre-multiply all color components and
- // then divide after calculations?
- let alpha = (alpha * 1000.0).round() / 1000.0;
-
- let mut result = AbsoluteColor::new(
- color_space,
- ColorComponents(result[0], result[1], result[2]),
- alpha,
- );
-
- result.flags = result_flags;
- // If both sides are legacy RGB, then the result stays in legacy RGB.
- if !left_color.is_legacy_color() || !right_color.is_legacy_color() {
- result.flags.insert(ColorFlags::AS_COLOR_FUNCTION);
- }
-
- result
-}
-
-fn interpolate_premultiplied_component(
- left: f32,
- left_weight: f32,
- left_alpha: f32,
- right: f32,
- right_weight: f32,
- right_alpha: f32,
-) -> f32 {
- left * left_weight * left_alpha + right * right_weight * right_alpha
-}
-
-// Normalize hue into [0, 360)
-#[inline]
-fn normalize_hue(v: f32) -> f32 {
- v - 360. * (v / 360.).floor()
-}
-
-fn adjust_hue(left: &mut f32, right: &mut f32, hue_interpolation: HueInterpolationMethod) {
- // Adjust the hue angle as per
- // https://drafts.csswg.org/css-color/#hue-interpolation.
- //
- // If both hue angles are NAN, they should be set to 0. Otherwise, if a
- // single hue angle is NAN, it should use the other hue angle.
- if left.is_nan() {
- if right.is_nan() {
- *left = 0.;
- *right = 0.;
- } else {
- *left = *right;
- }
- } else if right.is_nan() {
- *right = *left;
- }
-
- if hue_interpolation == HueInterpolationMethod::Specified {
- // Angles are not adjusted. They are interpolated like any other
- // component.
- return;
- }
-
- *left = normalize_hue(*left);
- *right = normalize_hue(*right);
-
- match hue_interpolation {
- // https://drafts.csswg.org/css-color/#shorter
- HueInterpolationMethod::Shorter => {
- let delta = *right - *left;
-
- if delta > 180. {
- *left += 360.;
- } else if delta < -180. {
- *right += 360.;
- }
- },
- // https://drafts.csswg.org/css-color/#longer
- HueInterpolationMethod::Longer => {
- let delta = *right - *left;
- if 0. < delta && delta < 180. {
- *left += 360.;
- } else if -180. < delta && delta < 0. {
- *right += 360.;
- }
- },
- // https://drafts.csswg.org/css-color/#increasing
- HueInterpolationMethod::Increasing => {
- if *right < *left {
- *right += 360.;
- }
- },
- // https://drafts.csswg.org/css-color/#decreasing
- HueInterpolationMethod::Decreasing => {
- if *left < *right {
- *left += 360.;
- }
- },
- HueInterpolationMethod::Specified => unreachable!("Handled above"),
- }
-}
-
-fn interpolate_hue(
- mut left: f32,
- left_weight: f32,
- mut right: f32,
- right_weight: f32,
- hue_interpolation: HueInterpolationMethod,
-) -> f32 {
- adjust_hue(&mut left, &mut right, hue_interpolation);
- left * left_weight + right * right_weight
-}
-
-struct InterpolatedAlpha {
- /// The adjusted left alpha value.
- left: f32,
- /// The adjusted right alpha value.
- right: f32,
- /// The interpolated alpha value.
- interpolated: f32,
- /// Whether the alpha component should be `none`.
- is_none: bool,
-}
-
-fn interpolate_alpha(
- left: f32,
- left_weight: f32,
- right: f32,
- right_weight: f32,
- outcome: ComponentMixOutcome,
-) -> InterpolatedAlpha {
- // <https://drafts.csswg.org/css-color-4/#interpolation-missing>
- let mut result = match outcome {
- ComponentMixOutcome::Mix => {
- let interpolated = left * left_weight + right * right_weight;
- InterpolatedAlpha {
- left,
- right,
- interpolated,
- is_none: false,
- }
- },
- ComponentMixOutcome::UseLeft => InterpolatedAlpha {
- left,
- right: left,
- interpolated: left,
- is_none: false,
- },
- ComponentMixOutcome::UseRight => InterpolatedAlpha {
- left: right,
- right,
- interpolated: right,
- is_none: false,
- },
- ComponentMixOutcome::None => InterpolatedAlpha {
- left: 1.0,
- right: 1.0,
- interpolated: 0.0,
- is_none: true,
- },
- };
-
- // Clip all alpha values to [0.0..1.0].
- result.left = result.left.clamp(0.0, 1.0);
- result.right = result.right.clamp(0.0, 1.0);
- result.interpolated = result.interpolated.clamp(0.0, 1.0);
-
- result
-}
-
-fn interpolate_premultiplied(
- left: &[f32; 4],
- left_weight: f32,
- right: &[f32; 4],
- right_weight: f32,
- hue_index: Option<usize>,
- hue_interpolation: HueInterpolationMethod,
- outcomes: &[ComponentMixOutcome; 4],
-) -> ([f32; 4], ColorFlags) {
- let alpha = interpolate_alpha(left[3], left_weight, right[3], right_weight, outcomes[3]);
- let mut flags = if alpha.is_none {
- ColorFlags::ALPHA_IS_NONE
- } else {
- ColorFlags::empty()
- };
-
- let mut result = [0.; 4];
-
- for i in 0..3 {
- match outcomes[i] {
- ComponentMixOutcome::Mix => {
- let is_hue = hue_index == Some(i);
- result[i] = if is_hue {
- normalize_hue(interpolate_hue(
- left[i],
- left_weight,
- right[i],
- right_weight,
- hue_interpolation,
- ))
- } else {
- let interpolated = interpolate_premultiplied_component(
- left[i],
- left_weight,
- alpha.left,
- right[i],
- right_weight,
- alpha.right,
- );
-
- if alpha.interpolated == 0.0 {
- interpolated
- } else {
- interpolated / alpha.interpolated
- }
- };
- },
- ComponentMixOutcome::UseLeft => result[i] = left[i],
- ComponentMixOutcome::UseRight => result[i] = right[i],
- ComponentMixOutcome::None => {
- result[i] = 0.0;
- match i {
- 0 => flags.insert(ColorFlags::C1_IS_NONE),
- 1 => flags.insert(ColorFlags::C2_IS_NONE),
- 2 => flags.insert(ColorFlags::C3_IS_NONE),
- _ => unreachable!(),
- }
- },
- }
- }
- result[3] = alpha.interpolated;
-
- (result, flags)
-}
diff --git a/components/style/color/mod.rs b/components/style/color/mod.rs
deleted file mode 100644
index f5a36aba2e8..00000000000
--- a/components/style/color/mod.rs
+++ /dev/null
@@ -1,465 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
-
-//! Color support functions.
-
-/// cbindgen:ignore
-pub mod convert;
-pub mod mix;
-
-use std::fmt::{self, Write};
-use style_traits::{CssWriter, ToCss};
-
-/// The 3 components that make up a color. (Does not include the alpha component)
-#[derive(Copy, Clone, Debug, MallocSizeOf, PartialEq, ToShmem)]
-#[repr(C)]
-pub struct ColorComponents(pub f32, pub f32, pub f32);
-
-impl ColorComponents {
- /// Apply a function to each of the 3 components of the color.
- pub fn map(self, f: impl Fn(f32) -> f32) -> Self {
- Self(f(self.0), f(self.1), f(self.2))
- }
-}
-
-/// A color space representation in the CSS specification.
-///
-/// https://drafts.csswg.org/css-color-4/#typedef-color-space
-#[derive(
- Clone,
- Copy,
- Debug,
- Eq,
- MallocSizeOf,
- Parse,
- PartialEq,
- ToAnimatedValue,
- ToComputedValue,
- ToCss,
- ToResolvedValue,
- ToShmem,
-)]
-#[repr(u8)]
-pub enum ColorSpace {
- /// A color specified in the sRGB color space with either the rgb/rgba(..)
- /// functions or the newer color(srgb ..) function. If the color(..)
- /// function is used, the AS_COLOR_FUNCTION flag will be set. Examples:
- /// "color(srgb 0.691 0.139 0.259)", "rgb(176, 35, 66)"
- Srgb = 0,
- /// A color specified in the Hsl notation in the sRGB color space, e.g.
- /// "hsl(289.18 93.136% 65.531%)"
- /// https://drafts.csswg.org/css-color-4/#the-hsl-notation
- Hsl,
- /// A color specified in the Hwb notation in the sRGB color space, e.g.
- /// "hwb(740deg 20% 30%)"
- /// https://drafts.csswg.org/css-color-4/#the-hwb-notation
- Hwb,
- /// A color specified in the Lab color format, e.g.
- /// "lab(29.2345% 39.3825 20.0664)".
- /// https://w3c.github.io/csswg-drafts/css-color-4/#lab-colors
- Lab,
- /// A color specified in the Lch color format, e.g.
- /// "lch(29.2345% 44.2 27)".
- /// https://w3c.github.io/csswg-drafts/css-color-4/#lch-colors
- Lch,
- /// A color specified in the Oklab color format, e.g.
- /// "oklab(40.101% 0.1147 0.0453)".
- /// https://w3c.github.io/csswg-drafts/css-color-4/#lab-colors
- Oklab,
- /// A color specified in the Oklch color format, e.g.
- /// "oklch(40.101% 0.12332 21.555)".
- /// https://w3c.github.io/csswg-drafts/css-color-4/#lch-colors
- Oklch,
- /// A color specified with the color(..) function and the "srgb-linear"
- /// color space, e.g. "color(srgb-linear 0.435 0.017 0.055)".
- SrgbLinear,
- /// A color specified with the color(..) function and the "display-p3"
- /// color space, e.g. "color(display-p3 0.84 0.19 0.72)".
- DisplayP3,
- /// A color specified with the color(..) function and the "a98-rgb" color
- /// space, e.g. "color(a98-rgb 0.44091 0.49971 0.37408)".
- A98Rgb,
- /// A color specified with the color(..) function and the "prophoto-rgb"
- /// color space, e.g. "color(prophoto-rgb 0.36589 0.41717 0.31333)".
- ProphotoRgb,
- /// A color specified with the color(..) function and the "rec2020" color
- /// space, e.g. "color(rec2020 0.42210 0.47580 0.35605)".
- Rec2020,
- /// A color specified with the color(..) function and the "xyz-d50" color
- /// space, e.g. "color(xyz-d50 0.2005 0.14089 0.4472)".
- XyzD50,
- /// A color specified with the color(..) function and the "xyz-d65" or "xyz"
- /// color space, e.g. "color(xyz-d65 0.21661 0.14602 0.59452)".
- /// NOTE: https://drafts.csswg.org/css-color-4/#resolving-color-function-values
- /// specifies that `xyz` is an alias for the `xyz-d65` color space.
- #[parse(aliases = "xyz")]
- XyzD65,
-}
-
-impl ColorSpace {
- /// Returns whether this is a `<rectangular-color-space>`.
- #[inline]
- pub fn is_rectangular(&self) -> bool {
- !self.is_polar()
- }
-
- /// Returns whether this is a `<polar-color-space>`.
- #[inline]
- pub fn is_polar(&self) -> bool {
- matches!(self, Self::Hsl | Self::Hwb | Self::Lch | Self::Oklch)
- }
-
- /// Returns an index of the hue component in the color space, otherwise
- /// `None`.
- #[inline]
- pub fn hue_index(&self) -> Option<usize> {
- match self {
- Self::Hsl | Self::Hwb => Some(0),
- Self::Lch | Self::Oklch => Some(2),
-
- _ => {
- debug_assert!(!self.is_polar());
- None
- },
- }
- }
-}
-
-bitflags! {
- /// Flags used when serializing colors.
- #[derive(Default, MallocSizeOf, ToShmem)]
- #[repr(C)]
- pub struct ColorFlags : u8 {
- /// If set, serializes sRGB colors into `color(srgb ...)` instead of
- /// `rgba(...)`.
- const AS_COLOR_FUNCTION = 1 << 0;
- /// Whether the 1st color component is `none`.
- const C1_IS_NONE = 1 << 1;
- /// Whether the 2nd color component is `none`.
- const C2_IS_NONE = 1 << 2;
- /// Whether the 3rd color component is `none`.
- const C3_IS_NONE = 1 << 3;
- /// Whether the alpha component is `none`.
- const ALPHA_IS_NONE = 1 << 4;
- }
-}
-
-/// An absolutely specified color, using either rgb(), rgba(), lab(), lch(),
-/// oklab(), oklch() or color().
-#[derive(Copy, Clone, Debug, MallocSizeOf, PartialEq, ToShmem)]
-#[repr(C)]
-pub struct AbsoluteColor {
- /// The 3 components that make up colors in any color space.
- pub components: ColorComponents,
- /// The alpha component of the color.
- pub alpha: f32,
- /// The current color space that the components represent.
- pub color_space: ColorSpace,
- /// Extra flags used durring serialization of this color.
- pub flags: ColorFlags,
-}
-
-/// Given an [`AbsoluteColor`], return the 4 float components as the type given,
-/// e.g.:
-///
-/// ```rust
-/// let srgb = AbsoluteColor::new(ColorSpace::Srgb, 1.0, 0.0, 0.0, 0.0);
-/// let floats = color_components_as!(&srgb, [f32; 4]); // [1.0, 0.0, 0.0, 0.0]
-/// ```
-macro_rules! color_components_as {
- ($c:expr, $t:ty) => {{
- // This macro is not an inline function, because we can't use the
- // generic type ($t) in a constant expression as per:
- // https://github.com/rust-lang/rust/issues/76560
- const_assert_eq!(std::mem::size_of::<$t>(), std::mem::size_of::<[f32; 4]>());
- const_assert_eq!(std::mem::align_of::<$t>(), std::mem::align_of::<[f32; 4]>());
- const_assert!(std::mem::size_of::<AbsoluteColor>() >= std::mem::size_of::<$t>());
- const_assert_eq!(
- std::mem::align_of::<AbsoluteColor>(),
- std::mem::align_of::<$t>()
- );
-
- std::mem::transmute::<&ColorComponents, &$t>(&$c.components)
- }};
-}
-
-impl AbsoluteColor {
- /// Create a new [`AbsoluteColor`] with the given [`ColorSpace`] and
- /// components.
- pub fn new(color_space: ColorSpace, components: ColorComponents, alpha: f32) -> Self {
- let mut components = components;
-
- // Lightness must not be less than 0.
- if matches!(
- color_space,
- ColorSpace::Lab | ColorSpace::Lch | ColorSpace::Oklab | ColorSpace::Oklch
- ) {
- components.0 = components.0.max(0.0);
- }
-
- // Chroma must not be less than 0.
- if matches!(color_space, ColorSpace::Lch | ColorSpace::Oklch) {
- components.1 = components.1.max(0.0);
- }
-
- Self {
- components,
- alpha: alpha.clamp(0.0, 1.0),
- color_space,
- flags: ColorFlags::empty(),
- }
- }
-
- /// Create a new [`AbsoluteColor`] from rgba values in the sRGB color space.
- pub fn srgb(red: f32, green: f32, blue: f32, alpha: f32) -> Self {
- Self::new(ColorSpace::Srgb, ColorComponents(red, green, blue), alpha)
- }
-
- /// Create a new transparent color.
- pub fn transparent() -> Self {
- Self::srgb(0.0, 0.0, 0.0, 0.0)
- }
-
- /// Create a new opaque black color.
- pub fn black() -> Self {
- Self::srgb(0.0, 0.0, 0.0, 1.0)
- }
-
- /// Create a new opaque white color.
- pub fn white() -> Self {
- Self::srgb(1.0, 1.0, 1.0, 1.0)
- }
-
- /// Return all the components of the color in an array. (Includes alpha)
- #[inline]
- pub fn raw_components(&self) -> &[f32; 4] {
- unsafe { color_components_as!(self, [f32; 4]) }
- }
-
- /// Returns true if this color is in one of the legacy color formats.
- #[inline]
- pub fn is_legacy_color(&self) -> bool {
- // rgb(), rgba(), hsl(), hsla(), hwb(), hwba()
- match self.color_space {
- ColorSpace::Srgb => !self.flags.contains(ColorFlags::AS_COLOR_FUNCTION),
- ColorSpace::Hsl | ColorSpace::Hwb => true,
- _ => false,
- }
- }
-
- /// Return the alpha component.
- #[inline]
- pub fn alpha(&self) -> f32 {
- self.alpha
- }
-
- /// Convert this color to the specified color space.
- pub fn to_color_space(&self, color_space: ColorSpace) -> Self {
- use ColorSpace::*;
-
- if self.color_space == color_space {
- return self.clone();
- }
-
- // We have simplified conversions that do not need to convert to XYZ
- // first. This improves performance, because it skips 2 matrix
- // multiplications and reduces float rounding errors.
- match (self.color_space, color_space) {
- (Srgb, Hsl) => {
- return Self::new(
- color_space,
- convert::rgb_to_hsl(&self.components),
- self.alpha,
- );
- },
-
- (Srgb, Hwb) => {
- return Self::new(
- color_space,
- convert::rgb_to_hwb(&self.components),
- self.alpha,
- );
- },
-
- (Hsl, Srgb) => {
- return Self::new(
- color_space,
- convert::hsl_to_rgb(&self.components),
- self.alpha,
- );
- },
-
- (Hwb, Srgb) => {
- return Self::new(
- color_space,
- convert::hwb_to_rgb(&self.components),
- self.alpha,
- );
- },
-
- (Lab, Lch) | (Oklab, Oklch) => {
- return Self::new(
- color_space,
- convert::lab_to_lch(&self.components),
- self.alpha,
- );
- },
-
- (Lch, Lab) | (Oklch, Oklab) => {
- return Self::new(
- color_space,
- convert::lch_to_lab(&self.components),
- self.alpha,
- );
- },
-
- _ => {},
- }
-
- let (xyz, white_point) = match self.color_space {
- Lab => convert::to_xyz::<convert::Lab>(&self.components),
- Lch => convert::to_xyz::<convert::Lch>(&self.components),
- Oklab => convert::to_xyz::<convert::Oklab>(&self.components),
- Oklch => convert::to_xyz::<convert::Oklch>(&self.components),
- Srgb => convert::to_xyz::<convert::Srgb>(&self.components),
- Hsl => convert::to_xyz::<convert::Hsl>(&self.components),
- Hwb => convert::to_xyz::<convert::Hwb>(&self.components),
- SrgbLinear => convert::to_xyz::<convert::SrgbLinear>(&self.components),
- DisplayP3 => convert::to_xyz::<convert::DisplayP3>(&self.components),
- A98Rgb => convert::to_xyz::<convert::A98Rgb>(&self.components),
- ProphotoRgb => convert::to_xyz::<convert::ProphotoRgb>(&self.components),
- Rec2020 => convert::to_xyz::<convert::Rec2020>(&self.components),
- XyzD50 => convert::to_xyz::<convert::XyzD50>(&self.components),
- XyzD65 => convert::to_xyz::<convert::XyzD65>(&self.components),
- };
-
- let result = match color_space {
- Lab => convert::from_xyz::<convert::Lab>(&xyz, white_point),
- Lch => convert::from_xyz::<convert::Lch>(&xyz, white_point),
- Oklab => convert::from_xyz::<convert::Oklab>(&xyz, white_point),
- Oklch => convert::from_xyz::<convert::Oklch>(&xyz, white_point),
- Srgb => convert::from_xyz::<convert::Srgb>(&xyz, white_point),
- Hsl => convert::from_xyz::<convert::Hsl>(&xyz, white_point),
- Hwb => convert::from_xyz::<convert::Hwb>(&xyz, white_point),
- SrgbLinear => convert::from_xyz::<convert::SrgbLinear>(&xyz, white_point),
- DisplayP3 => convert::from_xyz::<convert::DisplayP3>(&xyz, white_point),
- A98Rgb => convert::from_xyz::<convert::A98Rgb>(&xyz, white_point),
- ProphotoRgb => convert::from_xyz::<convert::ProphotoRgb>(&xyz, white_point),
- Rec2020 => convert::from_xyz::<convert::Rec2020>(&xyz, white_point),
- XyzD50 => convert::from_xyz::<convert::XyzD50>(&xyz, white_point),
- XyzD65 => convert::from_xyz::<convert::XyzD65>(&xyz, white_point),
- };
-
- Self::new(color_space, result, self.alpha)
- }
-}
-
-impl From<cssparser::PredefinedColorSpace> for ColorSpace {
- fn from(value: cssparser::PredefinedColorSpace) -> Self {
- match value {
- cssparser::PredefinedColorSpace::Srgb => ColorSpace::Srgb,
- cssparser::PredefinedColorSpace::SrgbLinear => ColorSpace::SrgbLinear,
- cssparser::PredefinedColorSpace::DisplayP3 => ColorSpace::DisplayP3,
- cssparser::PredefinedColorSpace::A98Rgb => ColorSpace::A98Rgb,
- cssparser::PredefinedColorSpace::ProphotoRgb => ColorSpace::ProphotoRgb,
- cssparser::PredefinedColorSpace::Rec2020 => ColorSpace::Rec2020,
- cssparser::PredefinedColorSpace::XyzD50 => ColorSpace::XyzD50,
- cssparser::PredefinedColorSpace::XyzD65 => ColorSpace::XyzD65,
- }
- }
-}
-
-impl ToCss for AbsoluteColor {
- fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
- where
- W: Write,
- {
- macro_rules! value_or_none {
- ($v:expr,$flag:tt) => {{
- if self.flags.contains(ColorFlags::$flag) {
- None
- } else {
- Some($v)
- }
- }};
- }
-
- let maybe_c1 = value_or_none!(self.components.0, C1_IS_NONE);
- let maybe_c2 = value_or_none!(self.components.1, C2_IS_NONE);
- let maybe_c3 = value_or_none!(self.components.2, C3_IS_NONE);
- let maybe_alpha = value_or_none!(self.alpha, ALPHA_IS_NONE);
-
- match self.color_space {
- ColorSpace::Hsl => {
- let rgb = convert::hsl_to_rgb(&self.components);
- Self::new(ColorSpace::Srgb, rgb, self.alpha).to_css(dest)
- },
-
- ColorSpace::Hwb => {
- let rgb = convert::hwb_to_rgb(&self.components);
-
- Self::new(ColorSpace::Srgb, rgb, self.alpha).to_css(dest)
- },
-
- ColorSpace::Srgb if !self.flags.contains(ColorFlags::AS_COLOR_FUNCTION) => {
- // Althought we are passing Option<_> in here, the to_css fn
- // knows that the "none" keyword is not supported in the
- // rgb/rgba legacy syntax.
- cssparser::ToCss::to_css(
- &cssparser::RGBA::from_floats(maybe_c1, maybe_c2, maybe_c3, maybe_alpha),
- dest,
- )
- },
- ColorSpace::Lab => cssparser::ToCss::to_css(
- &cssparser::Lab::new(maybe_c1, maybe_c2, maybe_c3, maybe_alpha),
- dest,
- ),
- ColorSpace::Lch => cssparser::ToCss::to_css(
- &cssparser::Lch::new(maybe_c1, maybe_c2, maybe_c3, maybe_alpha),
- dest,
- ),
- ColorSpace::Oklab => cssparser::ToCss::to_css(
- &cssparser::Oklab::new(maybe_c1, maybe_c2, maybe_c3, maybe_alpha),
- dest,
- ),
- ColorSpace::Oklch => cssparser::ToCss::to_css(
- &cssparser::Oklch::new(maybe_c1, maybe_c2, maybe_c3, maybe_alpha),
- dest,
- ),
- _ => {
- let color_space = match self.color_space {
- ColorSpace::Srgb => {
- debug_assert!(
- self.flags.contains(ColorFlags::AS_COLOR_FUNCTION),
- "The case without this flag should be handled in the wrapping match case!!"
- );
-
- cssparser::PredefinedColorSpace::Srgb
- },
- ColorSpace::SrgbLinear => cssparser::PredefinedColorSpace::SrgbLinear,
- ColorSpace::DisplayP3 => cssparser::PredefinedColorSpace::DisplayP3,
- ColorSpace::A98Rgb => cssparser::PredefinedColorSpace::A98Rgb,
- ColorSpace::ProphotoRgb => cssparser::PredefinedColorSpace::ProphotoRgb,
- ColorSpace::Rec2020 => cssparser::PredefinedColorSpace::Rec2020,
- ColorSpace::XyzD50 => cssparser::PredefinedColorSpace::XyzD50,
- ColorSpace::XyzD65 => cssparser::PredefinedColorSpace::XyzD65,
-
- _ => {
- unreachable!("other color spaces do not support color() syntax")
- },
- };
-
- let color_function = cssparser::ColorFunction {
- color_space,
- c1: maybe_c1,
- c2: maybe_c2,
- c3: maybe_c3,
- alpha: maybe_alpha,
- };
- let color = cssparser::Color::ColorFunction(color_function);
- cssparser::ToCss::to_css(&color, dest)
- },
- }
- }
-}
diff --git a/components/style/context.rs b/components/style/context.rs
deleted file mode 100644
index 8f717eca61f..00000000000
--- a/components/style/context.rs
+++ /dev/null
@@ -1,698 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
-
-//! The context within which style is calculated.
-
-#[cfg(feature = "servo")]
-use crate::animation::DocumentAnimationSet;
-use crate::bloom::StyleBloom;
-use crate::computed_value_flags::ComputedValueFlags;
-use crate::data::{EagerPseudoStyles, ElementData};
-use crate::dom::{SendElement, TElement};
-#[cfg(feature = "gecko")]
-use crate::gecko_bindings::structs;
-use crate::parallel::{STACK_SAFETY_MARGIN_KB, STYLE_THREAD_STACK_SIZE_KB};
-use crate::properties::ComputedValues;
-#[cfg(feature = "servo")]
-use crate::properties::PropertyId;
-use crate::rule_cache::RuleCache;
-use crate::rule_tree::StrongRuleNode;
-use crate::selector_parser::{SnapshotMap, EAGER_PSEUDO_COUNT};
-use crate::shared_lock::StylesheetGuards;
-use crate::sharing::StyleSharingCache;
-use crate::stylist::Stylist;
-use crate::thread_state::{self, ThreadState};
-use crate::traversal::DomTraversal;
-use crate::traversal_flags::TraversalFlags;
-use app_units::Au;
-use euclid::default::Size2D;
-use euclid::Scale;
-#[cfg(feature = "servo")]
-use fxhash::FxHashMap;
-use selectors::NthIndexCache;
-#[cfg(feature = "gecko")]
-use servo_arc::Arc;
-#[cfg(feature = "servo")]
-use servo_atoms::Atom;
-use std::fmt;
-use std::ops;
-use style_traits::CSSPixel;
-use style_traits::DevicePixel;
-#[cfg(feature = "servo")]
-use style_traits::SpeculativePainter;
-use time;
-
-pub use selectors::matching::QuirksMode;
-
-/// A global options structure for the style system. We use this instead of
-/// opts to abstract across Gecko and Servo.
-#[derive(Clone)]
-pub struct StyleSystemOptions {
- /// Whether the style sharing cache is disabled.
- pub disable_style_sharing_cache: bool,
- /// Whether we should dump statistics about the style system.
- pub dump_style_statistics: bool,
- /// The minimum number of elements that must be traversed to trigger a dump
- /// of style statistics.
- pub style_statistics_threshold: usize,
-}
-
-#[cfg(feature = "gecko")]
-fn get_env_bool(name: &str) -> bool {
- use std::env;
- match env::var(name) {
- Ok(s) => !s.is_empty(),
- Err(_) => false,
- }
-}
-
-const DEFAULT_STATISTICS_THRESHOLD: usize = 50;
-
-#[cfg(feature = "gecko")]
-fn get_env_usize(name: &str) -> Option<usize> {
- use std::env;
- env::var(name).ok().map(|s| {
- s.parse::<usize>()
- .expect("Couldn't parse environmental variable as usize")
- })
-}
-
-/// A global variable holding the state of
-/// `StyleSystemOptions::default().disable_style_sharing_cache`.
-/// See [#22854](https://github.com/servo/servo/issues/22854).
-#[cfg(feature = "servo")]
-pub static DEFAULT_DISABLE_STYLE_SHARING_CACHE: std::sync::atomic::AtomicBool =
- std::sync::atomic::AtomicBool::new(false);
-
-/// A global variable holding the state of
-/// `StyleSystemOptions::default().dump_style_statistics`.
-/// See [#22854](https://github.com/servo/servo/issues/22854).
-#[cfg(feature = "servo")]
-pub static DEFAULT_DUMP_STYLE_STATISTICS: std::sync::atomic::AtomicBool =
- std::sync::atomic::AtomicBool::new(false);
-
-impl Default for StyleSystemOptions {
- #[cfg(feature = "servo")]
- fn default() -> Self {
- use std::sync::atomic::Ordering;
-
- StyleSystemOptions {
- disable_style_sharing_cache: DEFAULT_DISABLE_STYLE_SHARING_CACHE
- .load(Ordering::Relaxed),
- dump_style_statistics: DEFAULT_DUMP_STYLE_STATISTICS.load(Ordering::Relaxed),
- style_statistics_threshold: DEFAULT_STATISTICS_THRESHOLD,
- }
- }
-
- #[cfg(feature = "gecko")]
- fn default() -> Self {
- StyleSystemOptions {
- disable_style_sharing_cache: get_env_bool("DISABLE_STYLE_SHARING_CACHE"),
- dump_style_statistics: get_env_bool("DUMP_STYLE_STATISTICS"),
- style_statistics_threshold: get_env_usize("STYLE_STATISTICS_THRESHOLD")
- .unwrap_or(DEFAULT_STATISTICS_THRESHOLD),
- }
- }
-}
-
-/// A shared style context.
-///
-/// There's exactly one of these during a given restyle traversal, and it's
-/// shared among the worker threads.
-pub struct SharedStyleContext<'a> {
- /// The CSS selector stylist.
- pub stylist: &'a Stylist,
-
- /// Whether visited styles are enabled.
- ///
- /// They may be disabled when Gecko's pref layout.css.visited_links_enabled
- /// is false, or when in private browsing mode.
- pub visited_styles_enabled: bool,
-
- /// Configuration options.
- pub options: StyleSystemOptions,
-
- /// Guards for pre-acquired locks
- pub guards: StylesheetGuards<'a>,
-
- /// The current time for transitions and animations. This is needed to ensure
- /// a consistent sampling time and also to adjust the time for testing.
- pub current_time_for_animations: f64,
-
- /// Flags controlling how we traverse the tree.
- pub traversal_flags: TraversalFlags,
-
- /// A map with our snapshots in order to handle restyle hints.
- pub snapshot_map: &'a SnapshotMap,
-
- /// The state of all animations for our styled elements.
- #[cfg(feature = "servo")]
- pub animations: DocumentAnimationSet,
-
- /// Paint worklets
- #[cfg(feature = "servo")]
- pub registered_speculative_painters: &'a dyn RegisteredSpeculativePainters,
-}
-
-impl<'a> SharedStyleContext<'a> {
- /// Return a suitable viewport size in order to be used for viewport units.
- pub fn viewport_size(&self) -> Size2D<Au> {
- self.stylist.device().au_viewport_size()
- }
-
- /// The device pixel ratio
- pub fn device_pixel_ratio(&self) -> Scale<f32, CSSPixel, DevicePixel> {
- self.stylist.device().device_pixel_ratio()
- }
-
- /// The quirks mode of the document.
- pub fn quirks_mode(&self) -> QuirksMode {
- self.stylist.quirks_mode()
- }
-}
-
-/// The structure holds various intermediate inputs that are eventually used by
-/// by the cascade.
-///
-/// The matching and cascading process stores them in this format temporarily
-/// within the `CurrentElementInfo`. At the end of the cascade, they are folded
-/// down into the main `ComputedValues` to reduce memory usage per element while
-/// still remaining accessible.
-#[derive(Clone, Debug, Default)]
-pub struct CascadeInputs {
- /// The rule node representing the ordered list of rules matched for this
- /// node.
- pub rules: Option<StrongRuleNode>,
-
- /// The rule node representing the ordered list of rules matched for this
- /// node if visited, only computed if there's a relevant link for this
- /// element. A element's "relevant link" is the element being matched if it
- /// is a link or the nearest ancestor link.
- pub visited_rules: Option<StrongRuleNode>,
-
- /// The set of flags from container queries that we need for invalidation.
- pub flags: ComputedValueFlags,
-}
-
-impl CascadeInputs {
- /// Construct inputs from previous cascade results, if any.
- pub fn new_from_style(style: &ComputedValues) -> Self {
- Self {
- rules: style.rules.clone(),
- visited_rules: style.visited_style().and_then(|v| v.rules.clone()),
- flags: style.flags.for_cascade_inputs(),
- }
- }
-}
-
-/// A list of cascade inputs for eagerly-cascaded pseudo-elements.
-/// The list is stored inline.
-#[derive(Debug)]
-pub struct EagerPseudoCascadeInputs(Option<[Option<CascadeInputs>; EAGER_PSEUDO_COUNT]>);
-
-// Manually implement `Clone` here because the derived impl of `Clone` for
-// array types assumes the value inside is `Copy`.
-impl Clone for EagerPseudoCascadeInputs {
- fn clone(&self) -> Self {
- if self.0.is_none() {
- return EagerPseudoCascadeInputs(None);
- }
- let self_inputs = self.0.as_ref().unwrap();
- let mut inputs: [Option<CascadeInputs>; EAGER_PSEUDO_COUNT] = Default::default();
- for i in 0..EAGER_PSEUDO_COUNT {
- inputs[i] = self_inputs[i].clone();
- }
- EagerPseudoCascadeInputs(Some(inputs))
- }
-}
-
-impl EagerPseudoCascadeInputs {
- /// Construct inputs from previous cascade results, if any.
- fn new_from_style(styles: &EagerPseudoStyles) -> Self {
- EagerPseudoCascadeInputs(styles.as_optional_array().map(|styles| {
- let mut inputs: [Option<CascadeInputs>; EAGER_PSEUDO_COUNT] = Default::default();
- for i in 0..EAGER_PSEUDO_COUNT {
- inputs[i] = styles[i].as_ref().map(|s| CascadeInputs::new_from_style(s));
- }
- inputs
- }))
- }
-
- /// Returns the list of rules, if they exist.
- pub fn into_array(self) -> Option<[Option<CascadeInputs>; EAGER_PSEUDO_COUNT]> {
- self.0
- }
-}
-
-/// The cascade inputs associated with a node, including those for any
-/// pseudo-elements.
-///
-/// The matching and cascading process stores them in this format temporarily
-/// within the `CurrentElementInfo`. At the end of the cascade, they are folded
-/// down into the main `ComputedValues` to reduce memory usage per element while
-/// still remaining accessible.
-#[derive(Clone, Debug)]
-pub struct ElementCascadeInputs {
- /// The element's cascade inputs.
- pub primary: CascadeInputs,
- /// A list of the inputs for the element's eagerly-cascaded pseudo-elements.
- pub pseudos: EagerPseudoCascadeInputs,
-}
-
-impl ElementCascadeInputs {
- /// Construct inputs from previous cascade results, if any.
- #[inline]
- pub fn new_from_element_data(data: &ElementData) -> Self {
- debug_assert!(data.has_styles());
- ElementCascadeInputs {
- primary: CascadeInputs::new_from_style(data.styles.primary()),
- pseudos: EagerPseudoCascadeInputs::new_from_style(&data.styles.pseudos),
- }
- }
-}
-
-/// Statistics gathered during the traversal. We gather statistics on each
-/// thread and then combine them after the threads join via the Add
-/// implementation below.
-#[derive(AddAssign, Clone, Default)]
-pub struct PerThreadTraversalStatistics {
- /// The total number of elements traversed.
- pub elements_traversed: u32,
- /// The number of elements where has_styles() went from false to true.
- pub elements_styled: u32,
- /// The number of elements for which we performed selector matching.
- pub elements_matched: u32,
- /// The number of cache hits from the StyleSharingCache.
- pub styles_shared: u32,
- /// The number of styles reused via rule node comparison from the
- /// StyleSharingCache.
- pub styles_reused: u32,
-}
-
-/// Statistics gathered during the traversal plus some information from
-/// other sources including stylist.
-#[derive(Default)]
-pub struct TraversalStatistics {
- /// Aggregated statistics gathered during the traversal.
- pub aggregated: PerThreadTraversalStatistics,
- /// The number of selectors in the stylist.
- pub selectors: u32,
- /// The number of revalidation selectors.
- pub revalidation_selectors: u32,
- /// The number of state/attr dependencies in the dependency set.
- pub dependency_selectors: u32,
- /// The number of declarations in the stylist.
- pub declarations: u32,
- /// The number of times the stylist was rebuilt.
- pub stylist_rebuilds: u32,
- /// Time spent in the traversal, in milliseconds.
- pub traversal_time_ms: f64,
- /// Whether this was a parallel traversal.
- pub is_parallel: bool,
- /// Whether this is a "large" traversal.
- pub is_large: bool,
-}
-
-/// Format the statistics in a way that the performance test harness understands.
-/// See https://bugzilla.mozilla.org/show_bug.cgi?id=1331856#c2
-impl fmt::Display for TraversalStatistics {
- fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
- debug_assert!(
- self.traversal_time_ms != 0.0,
- "should have set traversal time"
- );
- writeln!(f, "[PERF] perf block start")?;
- writeln!(
- f,
- "[PERF],traversal,{}",
- if self.is_parallel {
- "parallel"
- } else {
- "sequential"
- }
- )?;
- writeln!(
- f,
- "[PERF],elements_traversed,{}",
- self.aggregated.elements_traversed
- )?;
- writeln!(
- f,
- "[PERF],elements_styled,{}",
- self.aggregated.elements_styled
- )?;
- writeln!(
- f,
- "[PERF],elements_matched,{}",
- self.aggregated.elements_matched
- )?;
- writeln!(f, "[PERF],styles_shared,{}", self.aggregated.styles_shared)?;
- writeln!(f, "[PERF],styles_reused,{}", self.aggregated.styles_reused)?;
- writeln!(f, "[PERF],selectors,{}", self.selectors)?;
- writeln!(
- f,
- "[PERF],revalidation_selectors,{}",
- self.revalidation_selectors
- )?;
- writeln!(
- f,
- "[PERF],dependency_selectors,{}",
- self.dependency_selectors
- )?;
- writeln!(f, "[PERF],declarations,{}", self.declarations)?;
- writeln!(f, "[PERF],stylist_rebuilds,{}", self.stylist_rebuilds)?;
- writeln!(f, "[PERF],traversal_time_ms,{}", self.traversal_time_ms)?;
- writeln!(f, "[PERF] perf block end")
- }
-}
-
-impl TraversalStatistics {
- /// Generate complete traversal statistics.
- ///
- /// The traversal time is computed given the start time in seconds.
- pub fn new<E, D>(
- aggregated: PerThreadTraversalStatistics,
- traversal: &D,
- parallel: bool,
- start: f64,
- ) -> TraversalStatistics
- where
- E: TElement,
- D: DomTraversal<E>,
- {
- let threshold = traversal
- .shared_context()
- .options
- .style_statistics_threshold;
- let stylist = traversal.shared_context().stylist;
- let is_large = aggregated.elements_traversed as usize >= threshold;
- TraversalStatistics {
- aggregated,
- selectors: stylist.num_selectors() as u32,
- revalidation_selectors: stylist.num_revalidation_selectors() as u32,
- dependency_selectors: stylist.num_invalidations() as u32,
- declarations: stylist.num_declarations() as u32,
- stylist_rebuilds: stylist.num_rebuilds() as u32,
- traversal_time_ms: (time::precise_time_s() - start) * 1000.0,
- is_parallel: parallel,
- is_large,
- }
- }
-}
-
-#[cfg(feature = "gecko")]
-bitflags! {
- /// Represents which tasks are performed in a SequentialTask of
- /// UpdateAnimations which is a result of normal restyle.
- pub struct UpdateAnimationsTasks: u8 {
- /// Update CSS Animations.
- const CSS_ANIMATIONS = structs::UpdateAnimationsTasks_CSSAnimations;
- /// Update CSS Transitions.
- const CSS_TRANSITIONS = structs::UpdateAnimationsTasks_CSSTransitions;
- /// Update effect properties.
- const EFFECT_PROPERTIES = structs::UpdateAnimationsTasks_EffectProperties;
- /// Update animation cacade results for animations running on the compositor.
- const CASCADE_RESULTS = structs::UpdateAnimationsTasks_CascadeResults;
- /// Display property was changed from none.
- /// Script animations keep alive on display:none elements, so we need to trigger
- /// the second animation restyles for the script animations in the case where
- /// the display property was changed from 'none' to others.
- const DISPLAY_CHANGED_FROM_NONE = structs::UpdateAnimationsTasks_DisplayChangedFromNone;
- /// Update CSS named scroll progress timelines.
- const SCROLL_TIMELINES = structs::UpdateAnimationsTasks_ScrollTimelines;
- /// Update CSS named view progress timelines.
- const VIEW_TIMELINES = structs::UpdateAnimationsTasks_ViewTimelines;
- }
-}
-
-#[cfg(feature = "gecko")]
-bitflags! {
- /// Represents which tasks are performed in a SequentialTask as a result of
- /// animation-only restyle.
- pub struct PostAnimationTasks: u8 {
- /// Display property was changed from none in animation-only restyle so
- /// that we need to resolve styles for descendants in a subsequent
- /// normal restyle.
- const DISPLAY_CHANGED_FROM_NONE_FOR_SMIL = 0x01;
- }
-}
-
-/// A task to be run in sequential mode on the parent (non-worker) thread. This
-/// is used by the style system to queue up work which is not safe to do during
-/// the parallel traversal.
-pub enum SequentialTask<E: TElement> {
- /// Entry to avoid an unused type parameter error on servo.
- Unused(SendElement<E>),
-
- /// Performs one of a number of possible tasks related to updating
- /// animations based on the |tasks| field. These include updating CSS
- /// animations/transitions that changed as part of the non-animation style
- /// traversal, and updating the computed effect properties.
- #[cfg(feature = "gecko")]
- UpdateAnimations {
- /// The target element or pseudo-element.
- el: SendElement<E>,
- /// The before-change style for transitions. We use before-change style
- /// as the initial value of its Keyframe. Required if |tasks| includes
- /// CSSTransitions.
- before_change_style: Option<Arc<ComputedValues>>,
- /// The tasks which are performed in this SequentialTask.
- tasks: UpdateAnimationsTasks,
- },
-
- /// Performs one of a number of possible tasks as a result of animation-only
- /// restyle.
- ///
- /// Currently we do only process for resolving descendant elements that were
- /// display:none subtree for SMIL animation.
- #[cfg(feature = "gecko")]
- PostAnimation {
- /// The target element.
- el: SendElement<E>,
- /// The tasks which are performed in this SequentialTask.
- tasks: PostAnimationTasks,
- },
-}
-
-impl<E: TElement> SequentialTask<E> {
- /// Executes this task.
- pub fn execute(self) {
- use self::SequentialTask::*;
- debug_assert!(thread_state::get().contains(ThreadState::LAYOUT));
- match self {
- Unused(_) => unreachable!(),
- #[cfg(feature = "gecko")]
- UpdateAnimations {
- el,
- before_change_style,
- tasks,
- } => {
- el.update_animations(before_change_style, tasks);
- },
- #[cfg(feature = "gecko")]
- PostAnimation { el, tasks } => {
- el.process_post_animation(tasks);
- },
- }
- }
-
- /// Creates a task to update various animation-related state on a given
- /// (pseudo-)element.
- #[cfg(feature = "gecko")]
- pub fn update_animations(
- el: E,
- before_change_style: Option<Arc<ComputedValues>>,
- tasks: UpdateAnimationsTasks,
- ) -> Self {
- use self::SequentialTask::*;
- UpdateAnimations {
- el: unsafe { SendElement::new(el) },
- before_change_style,
- tasks,
- }
- }
-
- /// Creates a task to do post-process for a given element as a result of
- /// animation-only restyle.
- #[cfg(feature = "gecko")]
- pub fn process_post_animation(el: E, tasks: PostAnimationTasks) -> Self {
- use self::SequentialTask::*;
- PostAnimation {
- el: unsafe { SendElement::new(el) },
- tasks,
- }
- }
-}
-
-/// A list of SequentialTasks that get executed on Drop.
-pub struct SequentialTaskList<E>(Vec<SequentialTask<E>>)
-where
- E: TElement;
-
-impl<E> ops::Deref for SequentialTaskList<E>
-where
- E: TElement,
-{
- type Target = Vec<SequentialTask<E>>;
-
- fn deref(&self) -> &Self::Target {
- &self.0
- }
-}
-
-impl<E> ops::DerefMut for SequentialTaskList<E>
-where
- E: TElement,
-{
- fn deref_mut(&mut self) -> &mut Self::Target {
- &mut self.0
- }
-}
-
-impl<E> Drop for SequentialTaskList<E>
-where
- E: TElement,
-{
- fn drop(&mut self) {
- debug_assert!(thread_state::get().contains(ThreadState::LAYOUT));
- for task in self.0.drain(..) {
- task.execute()
- }
- }
-}
-
-/// A helper type for stack limit checking. This assumes that stacks grow
-/// down, which is true for all non-ancient CPU architectures.
-pub struct StackLimitChecker {
- lower_limit: usize,
-}
-
-impl StackLimitChecker {
- /// Create a new limit checker, for this thread, allowing further use
- /// of up to |stack_size| bytes beyond (below) the current stack pointer.
- #[inline(never)]
- pub fn new(stack_size_limit: usize) -> Self {
- StackLimitChecker {
- lower_limit: StackLimitChecker::get_sp() - stack_size_limit,
- }
- }
-
- /// Checks whether the previously stored stack limit has now been exceeded.
- #[inline(never)]
- pub fn limit_exceeded(&self) -> bool {
- let curr_sp = StackLimitChecker::get_sp();
-
- // Do some sanity-checking to ensure that our invariants hold, even in
- // the case where we've exceeded the soft limit.
- //
- // The correctness of depends on the assumption that no stack wraps
- // around the end of the address space.
- if cfg!(debug_assertions) {
- // Compute the actual bottom of the stack by subtracting our safety
- // margin from our soft limit. Note that this will be slightly below
- // the actual bottom of the stack, because there are a few initial
- // frames on the stack before we do the measurement that computes
- // the limit.
- let stack_bottom = self.lower_limit - STACK_SAFETY_MARGIN_KB * 1024;
-
- // The bottom of the stack should be below the current sp. If it
- // isn't, that means we've either waited too long to check the limit
- // and burned through our safety margin (in which case we probably
- // would have segfaulted by now), or we're using a limit computed for
- // a different thread.
- debug_assert!(stack_bottom < curr_sp);
-
- // Compute the distance between the current sp and the bottom of
- // the stack, and compare it against the current stack. It should be
- // no further from us than the total stack size. We allow some slop
- // to handle the fact that stack_bottom is a bit further than the
- // bottom of the stack, as discussed above.
- let distance_to_stack_bottom = curr_sp - stack_bottom;
- let max_allowable_distance = (STYLE_THREAD_STACK_SIZE_KB + 10) * 1024;
- debug_assert!(distance_to_stack_bottom <= max_allowable_distance);
- }
-
- // The actual bounds check.
- curr_sp <= self.lower_limit
- }
-
- // Technically, rustc can optimize this away, but shouldn't for now.
- // We should fix this once black_box is stable.
- #[inline(always)]
- fn get_sp() -> usize {
- let mut foo: usize = 42;
- (&mut foo as *mut usize) as usize
- }
-}
-
-/// A thread-local style context.
-///
-/// This context contains data that needs to be used during restyling, but is
-/// not required to be unique among worker threads, so we create one per worker
-/// thread in order to be able to mutate it without locking.
-pub struct ThreadLocalStyleContext<E: TElement> {
- /// A cache to share style among siblings.
- pub sharing_cache: StyleSharingCache<E>,
- /// A cache from matched properties to elements that match those.
- pub rule_cache: RuleCache,
- /// The bloom filter used to fast-reject selector-matching.
- pub bloom_filter: StyleBloom<E>,
- /// A set of tasks to be run (on the parent thread) in sequential mode after
- /// the rest of the styling is complete. This is useful for
- /// infrequently-needed non-threadsafe operations.
- ///
- /// It's important that goes after the style sharing cache and the bloom
- /// filter, to ensure they're dropped before we execute the tasks, which
- /// could create another ThreadLocalStyleContext for style computation.
- pub tasks: SequentialTaskList<E>,
- /// Statistics about the traversal.
- pub statistics: PerThreadTraversalStatistics,
- /// A checker used to ensure that parallel.rs does not recurse indefinitely
- /// even on arbitrarily deep trees. See Gecko bug 1376883.
- pub stack_limit_checker: StackLimitChecker,
- /// A cache for nth-index-like selectors.
- pub nth_index_cache: NthIndexCache,
-}
-
-impl<E: TElement> ThreadLocalStyleContext<E> {
- /// Creates a new `ThreadLocalStyleContext`
- pub fn new() -> Self {
- ThreadLocalStyleContext {
- sharing_cache: StyleSharingCache::new(),
- rule_cache: RuleCache::new(),
- bloom_filter: StyleBloom::new(),
- tasks: SequentialTaskList(Vec::new()),
- statistics: PerThreadTraversalStatistics::default(),
- stack_limit_checker: StackLimitChecker::new(
- (STYLE_THREAD_STACK_SIZE_KB - STACK_SAFETY_MARGIN_KB) * 1024,
- ),
- nth_index_cache: NthIndexCache::default(),
- }
- }
-}
-
-/// A `StyleContext` is just a simple container for a immutable reference to a
-/// shared style context, and a mutable reference to a local one.
-pub struct StyleContext<'a, E: TElement + 'a> {
- /// The shared style context reference.
- pub shared: &'a SharedStyleContext<'a>,
- /// The thread-local style context (mutable) reference.
- pub thread_local: &'a mut ThreadLocalStyleContext<E>,
-}
-
-/// A registered painter
-#[cfg(feature = "servo")]
-pub trait RegisteredSpeculativePainter: SpeculativePainter {
- /// The name it was registered with
- fn name(&self) -> Atom;
- /// The properties it was registered with
- fn properties(&self) -> &FxHashMap<Atom, PropertyId>;
-}
-
-/// A set of registered painters
-#[cfg(feature = "servo")]
-pub trait RegisteredSpeculativePainters: Sync {
- /// Look up a speculative painter
- fn get(&self, name: &Atom) -> Option<&dyn RegisteredSpeculativePainter>;
-}
diff --git a/components/style/counter_style/mod.rs b/components/style/counter_style/mod.rs
deleted file mode 100644
index 65143d69906..00000000000
--- a/components/style/counter_style/mod.rs
+++ /dev/null
@@ -1,697 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
-
-//! The [`@counter-style`][counter-style] at-rule.
-//!
-//! [counter-style]: https://drafts.csswg.org/css-counter-styles/
-
-use crate::error_reporting::ContextualParseError;
-use crate::parser::{Parse, ParserContext};
-use crate::shared_lock::{SharedRwLockReadGuard, ToCssWithGuard};
-use crate::str::CssStringWriter;
-use crate::values::specified::Integer;
-use crate::values::CustomIdent;
-use crate::Atom;
-use cssparser::{
- AtRuleParser, DeclarationParser, QualifiedRuleParser, RuleBodyItemParser, RuleBodyParser,
-};
-use cssparser::{CowRcStr, Parser, SourceLocation, Token};
-use selectors::parser::SelectorParseErrorKind;
-use std::fmt::{self, Write};
-use std::mem;
-use std::num::Wrapping;
-use style_traits::{Comma, CssWriter, OneOrMoreSeparated, ParseError};
-use style_traits::{StyleParseErrorKind, ToCss};
-
-/// Parse a counter style name reference.
-///
-/// This allows the reserved counter style names "decimal" and "disc".
-pub fn parse_counter_style_name<'i, 't>(
- input: &mut Parser<'i, 't>,
-) -> Result<CustomIdent, ParseError<'i>> {
- macro_rules! predefined {
- ($($name: expr,)+) => {
- {
- ascii_case_insensitive_phf_map! {
- // FIXME: use static atoms https://github.com/rust-lang/rust/issues/33156
- predefined -> &'static str = {
- $(
- $name => $name,
- )+
- }
- }
-
- let location = input.current_source_location();
- let ident = input.expect_ident()?;
- if let Some(&lower_cased) = predefined(&ident) {
- Ok(CustomIdent(Atom::from(lower_cased)))
- } else {
- // none is always an invalid <counter-style> value.
- CustomIdent::from_ident(location, ident, &["none"])
- }
- }
- }
- }
- include!("predefined.rs")
-}
-
-fn is_valid_name_definition(ident: &CustomIdent) -> bool {
- ident.0 != atom!("decimal") &&
- ident.0 != atom!("disc") &&
- ident.0 != atom!("circle") &&
- ident.0 != atom!("square") &&
- ident.0 != atom!("disclosure-closed") &&
- ident.0 != atom!("disclosure-open")
-}
-
-/// Parse the prelude of an @counter-style rule
-pub fn parse_counter_style_name_definition<'i, 't>(
- input: &mut Parser<'i, 't>,
-) -> Result<CustomIdent, ParseError<'i>> {
- parse_counter_style_name(input).and_then(|ident| {
- if !is_valid_name_definition(&ident) {
- Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError))
- } else {
- Ok(ident)
- }
- })
-}
-
-/// Parse the body (inside `{}`) of an @counter-style rule
-pub fn parse_counter_style_body<'i, 't>(
- name: CustomIdent,
- context: &ParserContext,
- input: &mut Parser<'i, 't>,
- location: SourceLocation,
-) -> Result<CounterStyleRuleData, ParseError<'i>> {
- let start = input.current_source_location();
- let mut rule = CounterStyleRuleData::empty(name, location);
- {
- let mut parser = CounterStyleRuleParser {
- context,
- rule: &mut rule,
- };
- let mut iter = RuleBodyParser::new(input, &mut parser);
- while let Some(declaration) = iter.next() {
- if let Err((error, slice)) = declaration {
- let location = error.location;
- let error = ContextualParseError::UnsupportedCounterStyleDescriptorDeclaration(
- slice, error,
- );
- context.log_css_error(location, error)
- }
- }
- }
- let error = match *rule.resolved_system() {
- ref system @ System::Cyclic |
- ref system @ System::Fixed { .. } |
- ref system @ System::Symbolic |
- ref system @ System::Alphabetic |
- ref system @ System::Numeric
- if rule.symbols.is_none() =>
- {
- let system = system.to_css_string();
- Some(ContextualParseError::InvalidCounterStyleWithoutSymbols(
- system,
- ))
- },
- ref system @ System::Alphabetic | ref system @ System::Numeric
- if rule.symbols().unwrap().0.len() < 2 =>
- {
- let system = system.to_css_string();
- Some(ContextualParseError::InvalidCounterStyleNotEnoughSymbols(
- system,
- ))
- },
- System::Additive if rule.additive_symbols.is_none() => {
- Some(ContextualParseError::InvalidCounterStyleWithoutAdditiveSymbols)
- },
- System::Extends(_) if rule.symbols.is_some() => {
- Some(ContextualParseError::InvalidCounterStyleExtendsWithSymbols)
- },
- System::Extends(_) if rule.additive_symbols.is_some() => {
- Some(ContextualParseError::InvalidCounterStyleExtendsWithAdditiveSymbols)
- },
- _ => None,
- };
- if let Some(error) = error {
- context.log_css_error(start, error);
- Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError))
- } else {
- Ok(rule)
- }
-}
-
-struct CounterStyleRuleParser<'a, 'b: 'a> {
- context: &'a ParserContext<'b>,
- rule: &'a mut CounterStyleRuleData,
-}
-
-/// Default methods reject all at rules.
-impl<'a, 'b, 'i> AtRuleParser<'i> for CounterStyleRuleParser<'a, 'b> {
- type Prelude = ();
- type AtRule = ();
- type Error = StyleParseErrorKind<'i>;
-}
-
-impl<'a, 'b, 'i> QualifiedRuleParser<'i> for CounterStyleRuleParser<'a, 'b> {
- type Prelude = ();
- type QualifiedRule = ();
- type Error = StyleParseErrorKind<'i>;
-}
-
-impl<'a, 'b, 'i> RuleBodyItemParser<'i, (), StyleParseErrorKind<'i>>
- for CounterStyleRuleParser<'a, 'b>
-{
- fn parse_qualified(&self) -> bool {
- false
- }
- fn parse_declarations(&self) -> bool {
- true
- }
-}
-
-macro_rules! checker {
- ($self:ident._($value:ident)) => {};
- ($self:ident. $checker:ident($value:ident)) => {
- if !$self.$checker(&$value) {
- return false;
- }
- };
-}
-
-macro_rules! counter_style_descriptors {
- (
- $( #[$doc: meta] $name: tt $ident: ident / $setter: ident [$checker: tt]: $ty: ty, )+
- ) => {
- /// An @counter-style rule
- #[derive(Clone, Debug, ToShmem)]
- pub struct CounterStyleRuleData {
- name: CustomIdent,
- generation: Wrapping<u32>,
- $(
- #[$doc]
- $ident: Option<$ty>,
- )+
- /// Line and column of the @counter-style rule source code.
- pub source_location: SourceLocation,
- }
-
- impl CounterStyleRuleData {
- fn empty(name: CustomIdent, source_location: SourceLocation) -> Self {
- CounterStyleRuleData {
- name: name,
- generation: Wrapping(0),
- $(
- $ident: None,
- )+
- source_location,
- }
- }
-
- $(
- #[$doc]
- pub fn $ident(&self) -> Option<&$ty> {
- self.$ident.as_ref()
- }
- )+
-
- $(
- #[$doc]
- pub fn $setter(&mut self, value: $ty) -> bool {
- checker!(self.$checker(value));
- self.$ident = Some(value);
- self.generation += Wrapping(1);
- true
- }
- )+
- }
-
- impl<'a, 'b, 'i> DeclarationParser<'i> for CounterStyleRuleParser<'a, 'b> {
- type Declaration = ();
- type Error = StyleParseErrorKind<'i>;
-
- fn parse_value<'t>(
- &mut self,
- name: CowRcStr<'i>,
- input: &mut Parser<'i, 't>,
- ) -> Result<(), ParseError<'i>> {
- match_ignore_ascii_case! { &*name,
- $(
- $name => {
- // DeclarationParser also calls parse_entirely so we’d normally not
- // need to, but in this case we do because we set the value as a side
- // effect rather than returning it.
- let value = input.parse_entirely(|i| Parse::parse(self.context, i))?;
- self.rule.$ident = Some(value)
- },
- )*
- _ => return Err(input.new_custom_error(SelectorParseErrorKind::UnexpectedIdent(name.clone()))),
- }
- Ok(())
- }
- }
-
- impl ToCssWithGuard for CounterStyleRuleData {
- fn to_css(&self, _guard: &SharedRwLockReadGuard, dest: &mut CssStringWriter) -> fmt::Result {
- dest.write_str("@counter-style ")?;
- self.name.to_css(&mut CssWriter::new(dest))?;
- dest.write_str(" { ")?;
- $(
- if let Some(ref value) = self.$ident {
- dest.write_str(concat!($name, ": "))?;
- ToCss::to_css(value, &mut CssWriter::new(dest))?;
- dest.write_str("; ")?;
- }
- )+
- dest.write_char('}')
- }
- }
- }
-}
-
-counter_style_descriptors! {
- /// <https://drafts.csswg.org/css-counter-styles/#counter-style-system>
- "system" system / set_system [check_system]: System,
-
- /// <https://drafts.csswg.org/css-counter-styles/#counter-style-negative>
- "negative" negative / set_negative [_]: Negative,
-
- /// <https://drafts.csswg.org/css-counter-styles/#counter-style-prefix>
- "prefix" prefix / set_prefix [_]: Symbol,
-
- /// <https://drafts.csswg.org/css-counter-styles/#counter-style-suffix>
- "suffix" suffix / set_suffix [_]: Symbol,
-
- /// <https://drafts.csswg.org/css-counter-styles/#counter-style-range>
- "range" range / set_range [_]: CounterRanges,
-
- /// <https://drafts.csswg.org/css-counter-styles/#counter-style-pad>
- "pad" pad / set_pad [_]: Pad,
-
- /// <https://drafts.csswg.org/css-counter-styles/#counter-style-fallback>
- "fallback" fallback / set_fallback [_]: Fallback,
-
- /// <https://drafts.csswg.org/css-counter-styles/#descdef-counter-style-symbols>
- "symbols" symbols / set_symbols [check_symbols]: Symbols,
-
- /// <https://drafts.csswg.org/css-counter-styles/#descdef-counter-style-additive-symbols>
- "additive-symbols" additive_symbols /
- set_additive_symbols [check_additive_symbols]: AdditiveSymbols,
-
- /// <https://drafts.csswg.org/css-counter-styles/#counter-style-speak-as>
- "speak-as" speak_as / set_speak_as [_]: SpeakAs,
-}
-
-// Implements the special checkers for some setters.
-// See <https://drafts.csswg.org/css-counter-styles/#the-csscounterstylerule-interface>
-impl CounterStyleRuleData {
- /// Check that the system is effectively not changed. Only params
- /// of system descriptor is changeable.
- fn check_system(&self, value: &System) -> bool {
- mem::discriminant(self.resolved_system()) == mem::discriminant(value)
- }
-
- fn check_symbols(&self, value: &Symbols) -> bool {
- match *self.resolved_system() {
- // These two systems require at least two symbols.
- System::Numeric | System::Alphabetic => value.0.len() >= 2,
- // No symbols should be set for extends system.
- System::Extends(_) => false,
- _ => true,
- }
- }
-
- fn check_additive_symbols(&self, _value: &AdditiveSymbols) -> bool {
- match *self.resolved_system() {
- // No additive symbols should be set for extends system.
- System::Extends(_) => false,
- _ => true,
- }
- }
-}
-
-impl CounterStyleRuleData {
- /// Get the name of the counter style rule.
- pub fn name(&self) -> &CustomIdent {
- &self.name
- }
-
- /// Set the name of the counter style rule. Caller must ensure that
- /// the name is valid.
- pub fn set_name(&mut self, name: CustomIdent) {
- debug_assert!(is_valid_name_definition(&name));
- self.name = name;
- }
-
- /// Get the current generation of the counter style rule.
- pub fn generation(&self) -> u32 {
- self.generation.0
- }
-
- /// Get the system of this counter style rule, default to
- /// `symbolic` if not specified.
- pub fn resolved_system(&self) -> &System {
- match self.system {
- Some(ref system) => system,
- None => &System::Symbolic,
- }
- }
-}
-
-/// <https://drafts.csswg.org/css-counter-styles/#counter-style-system>
-#[derive(Clone, Debug, ToShmem)]
-pub enum System {
- /// 'cyclic'
- Cyclic,
- /// 'numeric'
- Numeric,
- /// 'alphabetic'
- Alphabetic,
- /// 'symbolic'
- Symbolic,
- /// 'additive'
- Additive,
- /// 'fixed <integer>?'
- Fixed {
- /// '<integer>?'
- first_symbol_value: Option<Integer>,
- },
- /// 'extends <counter-style-name>'
- Extends(CustomIdent),
-}
-
-impl Parse for System {
- fn parse<'i, 't>(
- context: &ParserContext,
- input: &mut Parser<'i, 't>,
- ) -> Result<Self, ParseError<'i>> {
- try_match_ident_ignore_ascii_case! { input,
- "cyclic" => Ok(System::Cyclic),
- "numeric" => Ok(System::Numeric),
- "alphabetic" => Ok(System::Alphabetic),
- "symbolic" => Ok(System::Symbolic),
- "additive" => Ok(System::Additive),
- "fixed" => {
- let first_symbol_value = input.try_parse(|i| Integer::parse(context, i)).ok();
- Ok(System::Fixed { first_symbol_value })
- },
- "extends" => {
- let other = parse_counter_style_name(input)?;
- Ok(System::Extends(other))
- },
- }
- }
-}
-
-impl ToCss for System {
- fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
- where
- W: Write,
- {
- match *self {
- System::Cyclic => dest.write_str("cyclic"),
- System::Numeric => dest.write_str("numeric"),
- System::Alphabetic => dest.write_str("alphabetic"),
- System::Symbolic => dest.write_str("symbolic"),
- System::Additive => dest.write_str("additive"),
- System::Fixed { first_symbol_value } => {
- if let Some(value) = first_symbol_value {
- dest.write_str("fixed ")?;
- value.to_css(dest)
- } else {
- dest.write_str("fixed")
- }
- },
- System::Extends(ref other) => {
- dest.write_str("extends ")?;
- other.to_css(dest)
- },
- }
- }
-}
-
-/// <https://drafts.csswg.org/css-counter-styles/#typedef-symbol>
-#[derive(
- Clone, Debug, Eq, MallocSizeOf, PartialEq, ToComputedValue, ToResolvedValue, ToCss, ToShmem,
-)]
-#[repr(u8)]
-pub enum Symbol {
- /// <string>
- String(crate::OwnedStr),
- /// <custom-ident>
- Ident(CustomIdent),
- // Not implemented:
- // /// <image>
- // Image(Image),
-}
-
-impl Parse for Symbol {
- fn parse<'i, 't>(
- _context: &ParserContext,
- input: &mut Parser<'i, 't>,
- ) -> Result<Self, ParseError<'i>> {
- let location = input.current_source_location();
- match *input.next()? {
- Token::QuotedString(ref s) => Ok(Symbol::String(s.as_ref().to_owned().into())),
- Token::Ident(ref s) => Ok(Symbol::Ident(CustomIdent::from_ident(location, s, &[])?)),
- ref t => Err(location.new_unexpected_token_error(t.clone())),
- }
- }
-}
-
-impl Symbol {
- /// Returns whether this symbol is allowed in symbols() function.
- pub fn is_allowed_in_symbols(&self) -> bool {
- match self {
- // Identifier is not allowed.
- &Symbol::Ident(_) => false,
- _ => true,
- }
- }
-}
-
-/// <https://drafts.csswg.org/css-counter-styles/#counter-style-negative>
-#[derive(Clone, Debug, ToCss, ToShmem)]
-pub struct Negative(pub Symbol, pub Option<Symbol>);
-
-impl Parse for Negative {
- fn parse<'i, 't>(
- context: &ParserContext,
- input: &mut Parser<'i, 't>,
- ) -> Result<Self, ParseError<'i>> {
- Ok(Negative(
- Symbol::parse(context, input)?,
- input.try_parse(|input| Symbol::parse(context, input)).ok(),
- ))
- }
-}
-
-/// <https://drafts.csswg.org/css-counter-styles/#counter-style-range>
-#[derive(Clone, Debug, ToCss, ToShmem)]
-pub struct CounterRange {
- /// The start of the range.
- pub start: CounterBound,
- /// The end of the range.
- pub end: CounterBound,
-}
-
-/// <https://drafts.csswg.org/css-counter-styles/#counter-style-range>
-///
-/// Empty represents 'auto'
-#[derive(Clone, Debug, ToCss, ToShmem)]
-#[css(comma)]
-pub struct CounterRanges(#[css(iterable, if_empty = "auto")] pub crate::OwnedSlice<CounterRange>);
-
-/// A bound found in `CounterRanges`.
-#[derive(Clone, Copy, Debug, ToCss, ToShmem)]
-pub enum CounterBound {
- /// An integer bound.
- Integer(Integer),
- /// The infinite bound.
- Infinite,
-}
-
-impl Parse for CounterRanges {
- fn parse<'i, 't>(
- context: &ParserContext,
- input: &mut Parser<'i, 't>,
- ) -> Result<Self, ParseError<'i>> {
- if input
- .try_parse(|input| input.expect_ident_matching("auto"))
- .is_ok()
- {
- return Ok(CounterRanges(Default::default()));
- }
-
- let ranges = input.parse_comma_separated(|input| {
- let start = parse_bound(context, input)?;
- let end = parse_bound(context, input)?;
- if let (CounterBound::Integer(start), CounterBound::Integer(end)) = (start, end) {
- if start > end {
- return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError));
- }
- }
- Ok(CounterRange { start, end })
- })?;
-
- Ok(CounterRanges(ranges.into()))
- }
-}
-
-fn parse_bound<'i, 't>(
- context: &ParserContext,
- input: &mut Parser<'i, 't>,
-) -> Result<CounterBound, ParseError<'i>> {
- if let Ok(integer) = input.try_parse(|input| Integer::parse(context, input)) {
- return Ok(CounterBound::Integer(integer));
- }
- input.expect_ident_matching("infinite")?;
- Ok(CounterBound::Infinite)
-}
-
-/// <https://drafts.csswg.org/css-counter-styles/#counter-style-pad>
-#[derive(Clone, Debug, ToCss, ToShmem)]
-pub struct Pad(pub Integer, pub Symbol);
-
-impl Parse for Pad {
- fn parse<'i, 't>(
- context: &ParserContext,
- input: &mut Parser<'i, 't>,
- ) -> Result<Self, ParseError<'i>> {
- let pad_with = input.try_parse(|input| Symbol::parse(context, input));
- let min_length = Integer::parse_non_negative(context, input)?;
- let pad_with = pad_with.or_else(|_| Symbol::parse(context, input))?;
- Ok(Pad(min_length, pad_with))
- }
-}
-
-/// <https://drafts.csswg.org/css-counter-styles/#counter-style-fallback>
-#[derive(Clone, Debug, ToCss, ToShmem)]
-pub struct Fallback(pub CustomIdent);
-
-impl Parse for Fallback {
- fn parse<'i, 't>(
- _context: &ParserContext,
- input: &mut Parser<'i, 't>,
- ) -> Result<Self, ParseError<'i>> {
- Ok(Fallback(parse_counter_style_name(input)?))
- }
-}
-
-/// <https://drafts.csswg.org/css-counter-styles/#descdef-counter-style-symbols>
-#[derive(
- Clone, Debug, Eq, MallocSizeOf, PartialEq, ToComputedValue, ToResolvedValue, ToCss, ToShmem,
-)]
-#[repr(C)]
-pub struct Symbols(#[css(iterable)] pub crate::OwnedSlice<Symbol>);
-
-impl Parse for Symbols {
- fn parse<'i, 't>(
- context: &ParserContext,
- input: &mut Parser<'i, 't>,
- ) -> Result<Self, ParseError<'i>> {
- let mut symbols = Vec::new();
- while let Ok(s) = input.try_parse(|input| Symbol::parse(context, input)) {
- symbols.push(s);
- }
- if symbols.is_empty() {
- return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError));
- }
- Ok(Symbols(symbols.into()))
- }
-}
-
-/// <https://drafts.csswg.org/css-counter-styles/#descdef-counter-style-additive-symbols>
-#[derive(Clone, Debug, ToCss, ToShmem)]
-#[css(comma)]
-pub struct AdditiveSymbols(#[css(iterable)] pub crate::OwnedSlice<AdditiveTuple>);
-
-impl Parse for AdditiveSymbols {
- fn parse<'i, 't>(
- context: &ParserContext,
- input: &mut Parser<'i, 't>,
- ) -> Result<Self, ParseError<'i>> {
- let tuples = Vec::<AdditiveTuple>::parse(context, input)?;
- // FIXME maybe? https://github.com/w3c/csswg-drafts/issues/1220
- if tuples
- .windows(2)
- .any(|window| window[0].weight <= window[1].weight)
- {
- return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError));
- }
- Ok(AdditiveSymbols(tuples.into()))
- }
-}
-
-/// <integer> && <symbol>
-#[derive(Clone, Debug, ToCss, ToShmem)]
-pub struct AdditiveTuple {
- /// <integer>
- pub weight: Integer,
- /// <symbol>
- pub symbol: Symbol,
-}
-
-impl OneOrMoreSeparated for AdditiveTuple {
- type S = Comma;
-}
-
-impl Parse for AdditiveTuple {
- fn parse<'i, 't>(
- context: &ParserContext,
- input: &mut Parser<'i, 't>,
- ) -> Result<Self, ParseError<'i>> {
- let symbol = input.try_parse(|input| Symbol::parse(context, input));
- let weight = Integer::parse_non_negative(context, input)?;
- let symbol = symbol.or_else(|_| Symbol::parse(context, input))?;
- Ok(Self { weight, symbol })
- }
-}
-
-/// <https://drafts.csswg.org/css-counter-styles/#counter-style-speak-as>
-#[derive(Clone, Debug, ToCss, ToShmem)]
-pub enum SpeakAs {
- /// auto
- Auto,
- /// bullets
- Bullets,
- /// numbers
- Numbers,
- /// words
- Words,
- // /// spell-out, not supported, see bug 1024178
- // SpellOut,
- /// <counter-style-name>
- Other(CustomIdent),
-}
-
-impl Parse for SpeakAs {
- fn parse<'i, 't>(
- _context: &ParserContext,
- input: &mut Parser<'i, 't>,
- ) -> Result<Self, ParseError<'i>> {
- let mut is_spell_out = false;
- let result = input.try_parse(|input| {
- let ident = input.expect_ident().map_err(|_| ())?;
- match_ignore_ascii_case! { &*ident,
- "auto" => Ok(SpeakAs::Auto),
- "bullets" => Ok(SpeakAs::Bullets),
- "numbers" => Ok(SpeakAs::Numbers),
- "words" => Ok(SpeakAs::Words),
- "spell-out" => {
- is_spell_out = true;
- Err(())
- },
- _ => Err(()),
- }
- });
- if is_spell_out {
- // spell-out is not supported, but don’t parse it as a <counter-style-name>.
- // See bug 1024178.
- return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError));
- }
- result.or_else(|_| Ok(SpeakAs::Other(parse_counter_style_name(input)?)))
- }
-}
diff --git a/components/style/counter_style/predefined.rs b/components/style/counter_style/predefined.rs
deleted file mode 100644
index 7243e3b3f32..00000000000
--- a/components/style/counter_style/predefined.rs
+++ /dev/null
@@ -1,61 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
-
-predefined! {
- "decimal",
- "decimal-leading-zero",
- "arabic-indic",
- "armenian",
- "upper-armenian",
- "lower-armenian",
- "bengali",
- "cambodian",
- "khmer",
- "cjk-decimal",
- "devanagari",
- "georgian",
- "gujarati",
- "gurmukhi",
- "hebrew",
- "kannada",
- "lao",
- "malayalam",
- "mongolian",
- "myanmar",
- "oriya",
- "persian",
- "lower-roman",
- "upper-roman",
- "tamil",
- "telugu",
- "thai",
- "tibetan",
- "lower-alpha",
- "lower-latin",
- "upper-alpha",
- "upper-latin",
- "cjk-earthly-branch",
- "cjk-heavenly-stem",
- "lower-greek",
- "hiragana",
- "hiragana-iroha",
- "katakana",
- "katakana-iroha",
- "disc",
- "circle",
- "square",
- "disclosure-open",
- "disclosure-closed",
- "japanese-informal",
- "japanese-formal",
- "korean-hangul-formal",
- "korean-hanja-informal",
- "korean-hanja-formal",
- "simp-chinese-informal",
- "simp-chinese-formal",
- "trad-chinese-informal",
- "trad-chinese-formal",
- "cjk-ideographic",
- "ethiopic-numeric",
-}
diff --git a/components/style/counter_style/update_predefined.py b/components/style/counter_style/update_predefined.py
deleted file mode 100755
index 1523958ff3e..00000000000
--- a/components/style/counter_style/update_predefined.py
+++ /dev/null
@@ -1,35 +0,0 @@
-#!/usr/bin/env python
-
-# This Source Code Form is subject to the terms of the Mozilla Public
-# License, v. 2.0. If a copy of the MPL was not distributed with this
-# file, You can obtain one at https://mozilla.org/MPL/2.0/. */
-
-import os.path
-import re
-import urllib
-
-
-def main(filename):
- names = [
- re.search('>([^>]+)(</dfn>|<a class="self-link")', line).group(1)
- for line in urllib.urlopen("https://drafts.csswg.org/css-counter-styles/")
- if 'data-dfn-for="<counter-style-name>"' in line
- or 'data-dfn-for="<counter-style>"' in line
- ]
- with open(filename, "wb") as f:
- f.write(
- """\
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
-
-predefined! {
-"""
- )
- for name in names:
- f.write(' "%s",\n' % name)
- f.write("}\n")
-
-
-if __name__ == "__main__":
- main(os.path.join(os.path.dirname(__file__), "predefined.rs"))
diff --git a/components/style/custom_properties.rs b/components/style/custom_properties.rs
deleted file mode 100644
index f9516befc67..00000000000
--- a/components/style/custom_properties.rs
+++ /dev/null
@@ -1,1201 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
-
-//! Support for [custom properties for cascading variables][custom].
-//!
-//! [custom]: https://drafts.csswg.org/css-variables/
-
-use crate::applicable_declarations::CascadePriority;
-use crate::media_queries::Device;
-use crate::properties::{CSSWideKeyword, CustomDeclaration, CustomDeclarationValue};
-use crate::selector_map::{PrecomputedHashMap, PrecomputedHashSet, PrecomputedHasher};
-use crate::Atom;
-use cssparser::{
- CowRcStr, Delimiter, Parser, ParserInput, SourcePosition, Token, TokenSerializationType,
-};
-use indexmap::IndexMap;
-use selectors::parser::SelectorParseErrorKind;
-use servo_arc::Arc;
-use smallvec::SmallVec;
-use std::borrow::Cow;
-use std::cmp;
-use std::collections::hash_map::Entry;
-use std::fmt::{self, Write};
-use std::hash::BuildHasherDefault;
-use style_traits::{CssWriter, ParseError, StyleParseErrorKind, ToCss};
-
-/// The environment from which to get `env` function values.
-///
-/// TODO(emilio): If this becomes a bit more complex we should probably move it
-/// to the `media_queries` module, or something.
-#[derive(Debug, MallocSizeOf)]
-pub struct CssEnvironment;
-
-type EnvironmentEvaluator = fn(device: &Device) -> VariableValue;
-
-struct EnvironmentVariable {
- name: Atom,
- evaluator: EnvironmentEvaluator,
-}
-
-macro_rules! make_variable {
- ($name:expr, $evaluator:expr) => {{
- EnvironmentVariable {
- name: $name,
- evaluator: $evaluator,
- }
- }};
-}
-
-fn get_safearea_inset_top(device: &Device) -> VariableValue {
- VariableValue::pixels(device.safe_area_insets().top)
-}
-
-fn get_safearea_inset_bottom(device: &Device) -> VariableValue {
- VariableValue::pixels(device.safe_area_insets().bottom)
-}
-
-fn get_safearea_inset_left(device: &Device) -> VariableValue {
- VariableValue::pixels(device.safe_area_insets().left)
-}
-
-fn get_safearea_inset_right(device: &Device) -> VariableValue {
- VariableValue::pixels(device.safe_area_insets().right)
-}
-
-#[cfg(feature = "gecko")]
-fn get_content_preferred_color_scheme(device: &Device) -> VariableValue {
- use crate::gecko::media_features::PrefersColorScheme;
- let prefers_color_scheme = unsafe {
- crate::gecko_bindings::bindings::Gecko_MediaFeatures_PrefersColorScheme(
- device.document(),
- /* use_content = */ true,
- )
- };
- VariableValue::ident(match prefers_color_scheme {
- PrefersColorScheme::Light => "light",
- PrefersColorScheme::Dark => "dark",
- })
-}
-
-#[cfg(feature = "servo")]
-fn get_content_preferred_color_scheme(_device: &Device) -> VariableValue {
- // TODO: implement this.
- VariableValue::ident("light")
-}
-
-fn get_scrollbar_inline_size(device: &Device) -> VariableValue {
- VariableValue::pixels(device.scrollbar_inline_size().px())
-}
-
-static ENVIRONMENT_VARIABLES: [EnvironmentVariable; 4] = [
- make_variable!(atom!("safe-area-inset-top"), get_safearea_inset_top),
- make_variable!(atom!("safe-area-inset-bottom"), get_safearea_inset_bottom),
- make_variable!(atom!("safe-area-inset-left"), get_safearea_inset_left),
- make_variable!(atom!("safe-area-inset-right"), get_safearea_inset_right),
-];
-
-#[cfg(feature = "gecko")]
-macro_rules! lnf_int {
- ($id:ident) => {
- unsafe {
- crate::gecko_bindings::bindings::Gecko_GetLookAndFeelInt(
- crate::gecko_bindings::bindings::LookAndFeel_IntID::$id as i32,
- )
- }
- };
-}
-
-#[cfg(feature = "servo")]
-macro_rules! lnf_int {
- ($id:ident) => {
- // TODO: implement this.
- 0
- };
-}
-
-macro_rules! lnf_int_variable {
- ($atom:expr, $id:ident, $ctor:ident) => {{
- fn __eval(_: &Device) -> VariableValue {
- VariableValue::$ctor(lnf_int!($id))
- }
- make_variable!($atom, __eval)
- }};
-}
-
-static CHROME_ENVIRONMENT_VARIABLES: [EnvironmentVariable; 6] = [
- lnf_int_variable!(
- atom!("-moz-gtk-csd-titlebar-radius"),
- TitlebarRadius,
- int_pixels
- ),
- lnf_int_variable!(
- atom!("-moz-gtk-csd-close-button-position"),
- GTKCSDCloseButtonPosition,
- integer
- ),
- lnf_int_variable!(
- atom!("-moz-gtk-csd-minimize-button-position"),
- GTKCSDMinimizeButtonPosition,
- integer
- ),
- lnf_int_variable!(
- atom!("-moz-gtk-csd-maximize-button-position"),
- GTKCSDMaximizeButtonPosition,
- integer
- ),
- make_variable!(
- atom!("-moz-content-preferred-color-scheme"),
- get_content_preferred_color_scheme
- ),
- make_variable!(atom!("scrollbar-inline-size"), get_scrollbar_inline_size),
-];
-
-impl CssEnvironment {
- #[inline]
- fn get(&self, name: &Atom, device: &Device) -> Option<VariableValue> {
- if let Some(var) = ENVIRONMENT_VARIABLES.iter().find(|var| var.name == *name) {
- return Some((var.evaluator)(device));
- }
- if !device.chrome_rules_enabled_for_document() {
- return None;
- }
- let var = CHROME_ENVIRONMENT_VARIABLES
- .iter()
- .find(|var| var.name == *name)?;
- Some((var.evaluator)(device))
- }
-}
-
-/// A custom property name is just an `Atom`.
-///
-/// Note that this does not include the `--` prefix
-pub type Name = Atom;
-
-/// Parse a custom property name.
-///
-/// <https://drafts.csswg.org/css-variables/#typedef-custom-property-name>
-pub fn parse_name(s: &str) -> Result<&str, ()> {
- if s.starts_with("--") && s.len() > 2 {
- Ok(&s[2..])
- } else {
- Err(())
- }
-}
-
-/// A value for a custom property is just a set of tokens.
-///
-/// We preserve the original CSS for serialization, and also the variable
-/// references to other custom property names.
-#[derive(Clone, Debug, MallocSizeOf, PartialEq, ToShmem)]
-pub struct VariableValue {
- css: String,
-
- first_token_type: TokenSerializationType,
- last_token_type: TokenSerializationType,
-
- /// Whether a variable value has a reference to an environment variable.
- ///
- /// If this is the case, we need to perform variable substitution on the
- /// value.
- references_environment: bool,
-
- /// Custom property names in var() functions.
- references: Box<[Name]>,
-}
-
-impl ToCss for SpecifiedValue {
- fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
- where
- W: Write,
- {
- dest.write_str(&self.css)
- }
-}
-
-/// A map from CSS variable names to CSS variable computed values, used for
-/// resolving.
-///
-/// A consistent ordering is required for CSSDeclaration objects in the
-/// DOM. CSSDeclarations expose property names as indexed properties, which
-/// need to be stable. So we keep an array of property names which order is
-/// determined on the order that they are added to the name-value map.
-///
-/// The variable values are guaranteed to not have references to other
-/// properties.
-pub type CustomPropertiesMap =
- IndexMap<Name, Arc<VariableValue>, BuildHasherDefault<PrecomputedHasher>>;
-
-/// Both specified and computed values are VariableValues, the difference is
-/// whether var() functions are expanded.
-pub type SpecifiedValue = VariableValue;
-/// Both specified and computed values are VariableValues, the difference is
-/// whether var() functions are expanded.
-pub type ComputedValue = VariableValue;
-
-/// A struct holding information about the external references to that a custom
-/// property value may have.
-#[derive(Default)]
-struct VarOrEnvReferences {
- custom_property_references: PrecomputedHashSet<Name>,
- references_environment: bool,
-}
-
-impl VariableValue {
- fn empty() -> Self {
- Self {
- css: String::new(),
- last_token_type: TokenSerializationType::nothing(),
- first_token_type: TokenSerializationType::nothing(),
- references: Default::default(),
- references_environment: false,
- }
- }
-
- fn push<'i>(
- &mut self,
- input: &Parser<'i, '_>,
- css: &str,
- css_first_token_type: TokenSerializationType,
- css_last_token_type: TokenSerializationType,
- ) -> Result<(), ParseError<'i>> {
- /// Prevent values from getting terribly big since you can use custom
- /// properties exponentially.
- ///
- /// This number (2MB) is somewhat arbitrary, but silly enough that no
- /// reasonable page should hit it. We could limit by number of total
- /// substitutions, but that was very easy to work around in practice
- /// (just choose a larger initial value and boom).
- const MAX_VALUE_LENGTH_IN_BYTES: usize = 2 * 1024 * 1024;
-
- if self.css.len() + css.len() > MAX_VALUE_LENGTH_IN_BYTES {
- return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError));
- }
-
- // This happens e.g. between two subsequent var() functions:
- // `var(--a)var(--b)`.
- //
- // In that case, css_*_token_type is nonsensical.
- if css.is_empty() {
- return Ok(());
- }
-
- self.first_token_type.set_if_nothing(css_first_token_type);
- // If self.first_token_type was nothing,
- // self.last_token_type is also nothing and this will be false:
- if self
- .last_token_type
- .needs_separator_when_before(css_first_token_type)
- {
- self.css.push_str("/**/")
- }
- self.css.push_str(css);
- self.last_token_type = css_last_token_type;
- Ok(())
- }
-
- fn push_from<'i>(
- &mut self,
- input: &Parser<'i, '_>,
- position: (SourcePosition, TokenSerializationType),
- last_token_type: TokenSerializationType,
- ) -> Result<(), ParseError<'i>> {
- self.push(
- input,
- input.slice_from(position.0),
- position.1,
- last_token_type,
- )
- }
-
- fn push_variable<'i>(
- &mut self,
- input: &Parser<'i, '_>,
- variable: &ComputedValue,
- ) -> Result<(), ParseError<'i>> {
- debug_assert!(variable.references.is_empty());
- self.push(
- input,
- &variable.css,
- variable.first_token_type,
- variable.last_token_type,
- )
- }
-
- /// Parse a custom property value.
- pub fn parse<'i, 't>(input: &mut Parser<'i, 't>) -> Result<Arc<Self>, ParseError<'i>> {
- let mut references = VarOrEnvReferences::default();
-
- let (first_token_type, css, last_token_type) =
- parse_self_contained_declaration_value(input, Some(&mut references))?;
-
- let custom_property_references = references
- .custom_property_references
- .into_iter()
- .collect::<Vec<_>>()
- .into_boxed_slice();
-
- let mut css = css.into_owned();
- css.shrink_to_fit();
-
- Ok(Arc::new(VariableValue {
- css,
- first_token_type,
- last_token_type,
- references: custom_property_references,
- references_environment: references.references_environment,
- }))
- }
-
- /// Create VariableValue from an int.
- fn integer(number: i32) -> Self {
- Self::from_token(Token::Number {
- has_sign: false,
- value: number as f32,
- int_value: Some(number),
- })
- }
-
- /// Create VariableValue from an int.
- fn ident(ident: &'static str) -> Self {
- Self::from_token(Token::Ident(ident.into()))
- }
-
- /// Create VariableValue from a float amount of CSS pixels.
- fn pixels(number: f32) -> Self {
- // FIXME (https://github.com/servo/rust-cssparser/issues/266):
- // No way to get TokenSerializationType::Dimension without creating
- // Token object.
- Self::from_token(Token::Dimension {
- has_sign: false,
- value: number,
- int_value: None,
- unit: CowRcStr::from("px"),
- })
- }
-
- /// Create VariableValue from an integer amount of CSS pixels.
- fn int_pixels(number: i32) -> Self {
- Self::from_token(Token::Dimension {
- has_sign: false,
- value: number as f32,
- int_value: Some(number),
- unit: CowRcStr::from("px"),
- })
- }
-
- fn from_token(token: Token) -> Self {
- let token_type = token.serialization_type();
- let mut css = token.to_css_string();
- css.shrink_to_fit();
-
- VariableValue {
- css,
- first_token_type: token_type,
- last_token_type: token_type,
- references: Default::default(),
- references_environment: false,
- }
- }
-}
-
-/// Parse the value of a non-custom property that contains `var()` references.
-pub fn parse_non_custom_with_var<'i, 't>(
- input: &mut Parser<'i, 't>,
-) -> Result<(TokenSerializationType, Cow<'i, str>), ParseError<'i>> {
- let (first_token_type, css, _) = parse_self_contained_declaration_value(input, None)?;
- Ok((first_token_type, css))
-}
-
-fn parse_self_contained_declaration_value<'i, 't>(
- input: &mut Parser<'i, 't>,
- references: Option<&mut VarOrEnvReferences>,
-) -> Result<(TokenSerializationType, Cow<'i, str>, TokenSerializationType), ParseError<'i>> {
- let start_position = input.position();
- let mut missing_closing_characters = String::new();
- let (first, last) =
- parse_declaration_value(input, references, &mut missing_closing_characters)?;
- let mut css: Cow<str> = input.slice_from(start_position).into();
- if !missing_closing_characters.is_empty() {
- // Unescaped backslash at EOF in a quoted string is ignored.
- if css.ends_with("\\") && matches!(missing_closing_characters.as_bytes()[0], b'"' | b'\'') {
- css.to_mut().pop();
- }
- css.to_mut().push_str(&missing_closing_characters);
- }
- Ok((first, css, last))
-}
-
-/// <https://drafts.csswg.org/css-syntax-3/#typedef-declaration-value>
-fn parse_declaration_value<'i, 't>(
- input: &mut Parser<'i, 't>,
- references: Option<&mut VarOrEnvReferences>,
- missing_closing_characters: &mut String,
-) -> Result<(TokenSerializationType, TokenSerializationType), ParseError<'i>> {
- input.parse_until_before(Delimiter::Bang | Delimiter::Semicolon, |input| {
- parse_declaration_value_block(input, references, missing_closing_characters)
- })
-}
-
-/// Like parse_declaration_value, but accept `!` and `;` since they are only
-/// invalid at the top level
-fn parse_declaration_value_block<'i, 't>(
- input: &mut Parser<'i, 't>,
- mut references: Option<&mut VarOrEnvReferences>,
- missing_closing_characters: &mut String,
-) -> Result<(TokenSerializationType, TokenSerializationType), ParseError<'i>> {
- input.skip_whitespace();
- let mut token_start = input.position();
- let mut token = match input.next_including_whitespace_and_comments() {
- Ok(token) => token,
- Err(_) => {
- return Ok((
- TokenSerializationType::nothing(),
- TokenSerializationType::nothing(),
- ));
- },
- };
- let first_token_type = token.serialization_type();
- loop {
- macro_rules! nested {
- () => {
- input.parse_nested_block(|input| {
- parse_declaration_value_block(
- input,
- references.as_mut().map(|r| &mut **r),
- missing_closing_characters,
- )
- })?
- };
- }
- macro_rules! check_closed {
- ($closing:expr) => {
- if !input.slice_from(token_start).ends_with($closing) {
- missing_closing_characters.push_str($closing)
- }
- };
- }
- let last_token_type = match *token {
- Token::Comment(_) => {
- let serialization_type = token.serialization_type();
- let token_slice = input.slice_from(token_start);
- if !token_slice.ends_with("*/") {
- missing_closing_characters.push_str(if token_slice.ends_with('*') {
- "/"
- } else {
- "*/"
- })
- }
- serialization_type
- },
- Token::BadUrl(ref u) => {
- let e = StyleParseErrorKind::BadUrlInDeclarationValueBlock(u.clone());
- return Err(input.new_custom_error(e));
- },
- Token::BadString(ref s) => {
- let e = StyleParseErrorKind::BadStringInDeclarationValueBlock(s.clone());
- return Err(input.new_custom_error(e));
- },
- Token::CloseParenthesis => {
- let e = StyleParseErrorKind::UnbalancedCloseParenthesisInDeclarationValueBlock;
- return Err(input.new_custom_error(e));
- },
- Token::CloseSquareBracket => {
- let e = StyleParseErrorKind::UnbalancedCloseSquareBracketInDeclarationValueBlock;
- return Err(input.new_custom_error(e));
- },
- Token::CloseCurlyBracket => {
- let e = StyleParseErrorKind::UnbalancedCloseCurlyBracketInDeclarationValueBlock;
- return Err(input.new_custom_error(e));
- },
- Token::Function(ref name) => {
- if name.eq_ignore_ascii_case("var") {
- let args_start = input.state();
- input.parse_nested_block(|input| {
- parse_var_function(input, references.as_mut().map(|r| &mut **r))
- })?;
- input.reset(&args_start);
- } else if name.eq_ignore_ascii_case("env") {
- let args_start = input.state();
- input.parse_nested_block(|input| {
- parse_env_function(input, references.as_mut().map(|r| &mut **r))
- })?;
- input.reset(&args_start);
- }
- nested!();
- check_closed!(")");
- Token::CloseParenthesis.serialization_type()
- },
- Token::ParenthesisBlock => {
- nested!();
- check_closed!(")");
- Token::CloseParenthesis.serialization_type()
- },
- Token::CurlyBracketBlock => {
- nested!();
- check_closed!("}");
- Token::CloseCurlyBracket.serialization_type()
- },
- Token::SquareBracketBlock => {
- nested!();
- check_closed!("]");
- Token::CloseSquareBracket.serialization_type()
- },
- Token::QuotedString(_) => {
- let serialization_type = token.serialization_type();
- let token_slice = input.slice_from(token_start);
- let quote = &token_slice[..1];
- debug_assert!(matches!(quote, "\"" | "'"));
- if !(token_slice.ends_with(quote) && token_slice.len() > 1) {
- missing_closing_characters.push_str(quote)
- }
- serialization_type
- },
- Token::Ident(ref value) |
- Token::AtKeyword(ref value) |
- Token::Hash(ref value) |
- Token::IDHash(ref value) |
- Token::UnquotedUrl(ref value) |
- Token::Dimension {
- unit: ref value, ..
- } => {
- let serialization_type = token.serialization_type();
- let is_unquoted_url = matches!(token, Token::UnquotedUrl(_));
- if value.ends_with("�") && input.slice_from(token_start).ends_with("\\") {
- // Unescaped backslash at EOF in these contexts is interpreted as U+FFFD
- // Check the value in case the final backslash was itself escaped.
- // Serialize as escaped U+FFFD, which is also interpreted as U+FFFD.
- // (Unescaped U+FFFD would also work, but removing the backslash is annoying.)
- missing_closing_characters.push_str("�")
- }
- if is_unquoted_url {
- check_closed!(")");
- }
- serialization_type
- },
- _ => token.serialization_type(),
- };
-
- token_start = input.position();
- token = match input.next_including_whitespace_and_comments() {
- Ok(token) => token,
- Err(..) => return Ok((first_token_type, last_token_type)),
- };
- }
-}
-
-fn parse_fallback<'i, 't>(input: &mut Parser<'i, 't>) -> Result<(), ParseError<'i>> {
- // Exclude `!` and `;` at the top level
- // https://drafts.csswg.org/css-syntax/#typedef-declaration-value
- input.parse_until_before(Delimiter::Bang | Delimiter::Semicolon, |input| {
- // Skip until the end.
- while input.next_including_whitespace_and_comments().is_ok() {}
- Ok(())
- })
-}
-
-// If the var function is valid, return Ok((custom_property_name, fallback))
-fn parse_var_function<'i, 't>(
- input: &mut Parser<'i, 't>,
- references: Option<&mut VarOrEnvReferences>,
-) -> Result<(), ParseError<'i>> {
- let name = input.expect_ident_cloned()?;
- let name = parse_name(&name).map_err(|()| {
- input.new_custom_error(SelectorParseErrorKind::UnexpectedIdent(name.clone()))
- })?;
- if input.try_parse(|input| input.expect_comma()).is_ok() {
- parse_fallback(input)?;
- }
- if let Some(refs) = references {
- refs.custom_property_references.insert(Atom::from(name));
- }
- Ok(())
-}
-
-fn parse_env_function<'i, 't>(
- input: &mut Parser<'i, 't>,
- references: Option<&mut VarOrEnvReferences>,
-) -> Result<(), ParseError<'i>> {
- // TODO(emilio): This should be <custom-ident> per spec, but no other
- // browser does that, see https://github.com/w3c/csswg-drafts/issues/3262.
- input.expect_ident()?;
- if input.try_parse(|input| input.expect_comma()).is_ok() {
- parse_fallback(input)?;
- }
- if let Some(references) = references {
- references.references_environment = true;
- }
- Ok(())
-}
-
-/// A struct that takes care of encapsulating the cascade process for custom
-/// properties.
-pub struct CustomPropertiesBuilder<'a> {
- seen: PrecomputedHashSet<&'a Name>,
- may_have_cycles: bool,
- custom_properties: Option<CustomPropertiesMap>,
- inherited: Option<&'a Arc<CustomPropertiesMap>>,
- reverted: PrecomputedHashMap<&'a Name, (CascadePriority, bool)>,
- device: &'a Device,
-}
-
-impl<'a> CustomPropertiesBuilder<'a> {
- /// Create a new builder, inheriting from a given custom properties map.
- pub fn new(inherited: Option<&'a Arc<CustomPropertiesMap>>, device: &'a Device) -> Self {
- Self {
- seen: PrecomputedHashSet::default(),
- reverted: Default::default(),
- may_have_cycles: false,
- custom_properties: None,
- inherited,
- device,
- }
- }
-
- /// Cascade a given custom property declaration.
- pub fn cascade(&mut self, declaration: &'a CustomDeclaration, priority: CascadePriority) {
- let CustomDeclaration {
- ref name,
- ref value,
- } = *declaration;
-
- if let Some(&(reverted_priority, is_origin_revert)) = self.reverted.get(&name) {
- if !reverted_priority.allows_when_reverted(&priority, is_origin_revert) {
- return;
- }
- }
-
- let was_already_present = !self.seen.insert(name);
- if was_already_present {
- return;
- }
-
- if !self.value_may_affect_style(name, value) {
- return;
- }
-
- if self.custom_properties.is_none() {
- self.custom_properties = Some(match self.inherited {
- Some(inherited) => (**inherited).clone(),
- None => CustomPropertiesMap::default(),
- });
- }
-
- let map = self.custom_properties.as_mut().unwrap();
- match *value {
- CustomDeclarationValue::Value(ref unparsed_value) => {
- let has_references = !unparsed_value.references.is_empty();
- self.may_have_cycles |= has_references;
-
- // If the variable value has no references and it has an
- // environment variable here, perform substitution here instead
- // of forcing a full traversal in `substitute_all` afterwards.
- let value = if !has_references && unparsed_value.references_environment {
- let result = substitute_references_in_value(unparsed_value, &map, &self.device);
- match result {
- Ok(new_value) => new_value,
- Err(..) => {
- map.remove(name);
- return;
- },
- }
- } else {
- (*unparsed_value).clone()
- };
- map.insert(name.clone(), value);
- },
- CustomDeclarationValue::CSSWideKeyword(keyword) => match keyword {
- CSSWideKeyword::RevertLayer | CSSWideKeyword::Revert => {
- let origin_revert = keyword == CSSWideKeyword::Revert;
- self.seen.remove(name);
- self.reverted.insert(name, (priority, origin_revert));
- },
- CSSWideKeyword::Initial => {
- map.remove(name);
- },
- // handled in value_may_affect_style
- CSSWideKeyword::Unset | CSSWideKeyword::Inherit => unreachable!(),
- },
- }
- }
-
- fn value_may_affect_style(&self, name: &Name, value: &CustomDeclarationValue) -> bool {
- match *value {
- CustomDeclarationValue::CSSWideKeyword(CSSWideKeyword::Unset) |
- CustomDeclarationValue::CSSWideKeyword(CSSWideKeyword::Inherit) => {
- // Custom properties are inherited by default. So
- // explicit 'inherit' or 'unset' means we can just use
- // any existing value in the inherited CustomPropertiesMap.
- return false;
- },
- _ => {},
- }
-
- let existing_value = self
- .custom_properties
- .as_ref()
- .and_then(|m| m.get(name))
- .or_else(|| self.inherited.and_then(|m| m.get(name)));
-
- match (existing_value, value) {
- (None, &CustomDeclarationValue::CSSWideKeyword(CSSWideKeyword::Initial)) => {
- // The initial value of a custom property is the same as it
- // not existing in the map.
- return false;
- },
- (Some(existing_value), &CustomDeclarationValue::Value(ref value)) => {
- // Don't bother overwriting an existing inherited value with
- // the same specified value.
- if existing_value == value {
- return false;
- }
- },
- _ => {},
- }
-
- true
- }
-
- fn inherited_properties_match(&self, map: &CustomPropertiesMap) -> bool {
- let inherited = match self.inherited {
- Some(inherited) => inherited,
- None => return false,
- };
- if inherited.len() != map.len() {
- return false;
- }
- for name in self.seen.iter() {
- if inherited.get(*name) != map.get(*name) {
- return false;
- }
- }
- true
- }
-
- /// Returns the final map of applicable custom properties.
- ///
- /// If there was any specified property, we've created a new map and now we
- /// need to remove any potential cycles, and wrap it in an arc.
- ///
- /// Otherwise, just use the inherited custom properties map.
- pub fn build(mut self) -> Option<Arc<CustomPropertiesMap>> {
- let mut map = match self.custom_properties.take() {
- Some(m) => m,
- None => return self.inherited.cloned(),
- };
-
- if self.may_have_cycles {
- substitute_all(&mut map, &self.seen, self.device);
- }
-
- // Some pages apply a lot of redundant custom properties, see e.g.
- // bug 1758974 comment 5. Try to detect the case where the values
- // haven't really changed, and save some memory by reusing the inherited
- // map in that case.
- if self.inherited_properties_match(&map) {
- return self.inherited.cloned();
- }
-
- map.shrink_to_fit();
- Some(Arc::new(map))
- }
-}
-
-/// Resolve all custom properties to either substituted, invalid, or unset
-/// (meaning we should use the inherited value).
-///
-/// It does cycle dependencies removal at the same time as substitution.
-fn substitute_all(
- custom_properties_map: &mut CustomPropertiesMap,
- seen: &PrecomputedHashSet<&Name>,
- device: &Device,
-) {
- // The cycle dependencies removal in this function is a variant
- // of Tarjan's algorithm. It is mostly based on the pseudo-code
- // listed in
- // https://en.wikipedia.org/w/index.php?
- // title=Tarjan%27s_strongly_connected_components_algorithm&oldid=801728495
-
- /// Struct recording necessary information for each variable.
- #[derive(Debug)]
- struct VarInfo {
- /// The name of the variable. It will be taken to save addref
- /// when the corresponding variable is popped from the stack.
- /// This also serves as a mark for whether the variable is
- /// currently in the stack below.
- name: Option<Name>,
- /// If the variable is in a dependency cycle, lowlink represents
- /// a smaller index which corresponds to a variable in the same
- /// strong connected component, which is known to be accessible
- /// from this variable. It is not necessarily the root, though.
- lowlink: usize,
- }
- /// Context struct for traversing the variable graph, so that we can
- /// avoid referencing all the fields multiple times.
- #[derive(Debug)]
- struct Context<'a> {
- /// Number of variables visited. This is used as the order index
- /// when we visit a new unresolved variable.
- count: usize,
- /// The map from custom property name to its order index.
- index_map: PrecomputedHashMap<Name, usize>,
- /// Information of each variable indexed by the order index.
- var_info: SmallVec<[VarInfo; 5]>,
- /// The stack of order index of visited variables. It contains
- /// all unfinished strong connected components.
- stack: SmallVec<[usize; 5]>,
- map: &'a mut CustomPropertiesMap,
- /// To resolve the environment to substitute `env()` variables.
- device: &'a Device,
- }
-
- /// This function combines the traversal for cycle removal and value
- /// substitution. It returns either a signal None if this variable
- /// has been fully resolved (to either having no reference or being
- /// marked invalid), or the order index for the given name.
- ///
- /// When it returns, the variable corresponds to the name would be
- /// in one of the following states:
- /// * It is still in context.stack, which means it is part of an
- /// potentially incomplete dependency circle.
- /// * It has been removed from the map. It can be either that the
- /// substitution failed, or it is inside a dependency circle.
- /// When this function removes a variable from the map because
- /// of dependency circle, it would put all variables in the same
- /// strong connected component to the set together.
- /// * It doesn't have any reference, because either this variable
- /// doesn't have reference at all in specified value, or it has
- /// been completely resolved.
- /// * There is no such variable at all.
- fn traverse<'a>(name: &Name, context: &mut Context<'a>) -> Option<usize> {
- // Some shortcut checks.
- let (name, value) = {
- let value = context.map.get(name)?;
-
- // Nothing to resolve.
- if value.references.is_empty() {
- debug_assert!(
- !value.references_environment,
- "Should've been handled earlier"
- );
- return None;
- }
-
- // Whether this variable has been visited in this traversal.
- let key;
- match context.index_map.entry(name.clone()) {
- Entry::Occupied(entry) => {
- return Some(*entry.get());
- },
- Entry::Vacant(entry) => {
- key = entry.key().clone();
- entry.insert(context.count);
- },
- }
-
- // Hold a strong reference to the value so that we don't
- // need to keep reference to context.map.
- (key, value.clone())
- };
-
- // Add new entry to the information table.
- let index = context.count;
- context.count += 1;
- debug_assert_eq!(index, context.var_info.len());
- context.var_info.push(VarInfo {
- name: Some(name),
- lowlink: index,
- });
- context.stack.push(index);
-
- let mut self_ref = false;
- let mut lowlink = index;
- for next in value.references.iter() {
- let next_index = match traverse(next, context) {
- Some(index) => index,
- // There is nothing to do if the next variable has been
- // fully resolved at this point.
- None => {
- continue;
- },
- };
- let next_info = &context.var_info[next_index];
- if next_index > index {
- // The next variable has a larger index than us, so it
- // must be inserted in the recursive call above. We want
- // to get its lowlink.
- lowlink = cmp::min(lowlink, next_info.lowlink);
- } else if next_index == index {
- self_ref = true;
- } else if next_info.name.is_some() {
- // The next variable has a smaller order index and it is
- // in the stack, so we are at the same component.
- lowlink = cmp::min(lowlink, next_index);
- }
- }
-
- context.var_info[index].lowlink = lowlink;
- if lowlink != index {
- // This variable is in a loop, but it is not the root of
- // this strong connected component. We simply return for
- // now, and the root would remove it from the map.
- //
- // This cannot be removed from the map here, because
- // otherwise the shortcut check at the beginning of this
- // function would return the wrong value.
- return Some(index);
- }
-
- // This is the root of a strong-connected component.
- let mut in_loop = self_ref;
- let name;
- loop {
- let var_index = context
- .stack
- .pop()
- .expect("The current variable should still be in stack");
- let var_info = &mut context.var_info[var_index];
- // We should never visit the variable again, so it's safe
- // to take the name away, so that we don't do additional
- // reference count.
- let var_name = var_info
- .name
- .take()
- .expect("Variable should not be poped from stack twice");
- if var_index == index {
- name = var_name;
- break;
- }
- // Anything here is in a loop which can traverse to the
- // variable we are handling, so remove it from the map, it's invalid
- // at computed-value time.
- context.map.remove(&var_name);
- in_loop = true;
- }
- if in_loop {
- // This variable is in loop. Resolve to invalid.
- context.map.remove(&name);
- return None;
- }
-
- // Now we have shown that this variable is not in a loop, and all of its
- // dependencies should have been resolved. We can start substitution
- // now.
- let result = substitute_references_in_value(&value, &context.map, &context.device);
- match result {
- Ok(computed_value) => {
- context.map.insert(name, computed_value);
- },
- Err(..) => {
- // This is invalid, reset it to the guaranteed-invalid value.
- context.map.remove(&name);
- },
- }
-
- // All resolved, so return the signal value.
- None
- }
-
- // Note that `seen` doesn't contain names inherited from our parent, but
- // those can't have variable references (since we inherit the computed
- // variables) so we don't want to spend cycles traversing them anyway.
- for name in seen {
- let mut context = Context {
- count: 0,
- index_map: PrecomputedHashMap::default(),
- stack: SmallVec::new(),
- var_info: SmallVec::new(),
- map: custom_properties_map,
- device,
- };
- traverse(name, &mut context);
- }
-}
-
-/// Replace `var()` and `env()` functions in a pre-existing variable value.
-fn substitute_references_in_value<'i>(
- value: &'i VariableValue,
- custom_properties: &CustomPropertiesMap,
- device: &Device,
-) -> Result<Arc<ComputedValue>, ParseError<'i>> {
- debug_assert!(!value.references.is_empty() || value.references_environment);
-
- let mut input = ParserInput::new(&value.css);
- let mut input = Parser::new(&mut input);
- let mut position = (input.position(), value.first_token_type);
- let mut computed_value = ComputedValue::empty();
-
- let last_token_type = substitute_block(
- &mut input,
- &mut position,
- &mut computed_value,
- custom_properties,
- device,
- )?;
-
- computed_value.push_from(&input, position, last_token_type)?;
- computed_value.css.shrink_to_fit();
- Ok(Arc::new(computed_value))
-}
-
-/// Replace `var()` functions in an arbitrary bit of input.
-///
-/// If the variable has its initial value, the callback should return `Err(())`
-/// and leave `partial_computed_value` unchanged.
-///
-/// Otherwise, it should push the value of the variable (with its own `var()` functions replaced)
-/// to `partial_computed_value` and return `Ok(last_token_type of what was pushed)`
-///
-/// Return `Err(())` if `input` is invalid at computed-value time.
-/// or `Ok(last_token_type that was pushed to partial_computed_value)` otherwise.
-fn substitute_block<'i>(
- input: &mut Parser<'i, '_>,
- position: &mut (SourcePosition, TokenSerializationType),
- partial_computed_value: &mut ComputedValue,
- custom_properties: &CustomPropertiesMap,
- device: &Device,
-) -> Result<TokenSerializationType, ParseError<'i>> {
- let mut last_token_type = TokenSerializationType::nothing();
- let mut set_position_at_next_iteration = false;
- loop {
- let before_this_token = input.position();
- let next = input.next_including_whitespace_and_comments();
- if set_position_at_next_iteration {
- *position = (
- before_this_token,
- match next {
- Ok(token) => token.serialization_type(),
- Err(_) => TokenSerializationType::nothing(),
- },
- );
- set_position_at_next_iteration = false;
- }
- let token = match next {
- Ok(token) => token,
- Err(..) => break,
- };
- match token {
- Token::Function(ref name)
- if name.eq_ignore_ascii_case("var") || name.eq_ignore_ascii_case("env") =>
- {
- let is_env = name.eq_ignore_ascii_case("env");
-
- partial_computed_value.push(
- input,
- input.slice(position.0..before_this_token),
- position.1,
- last_token_type,
- )?;
- input.parse_nested_block(|input| {
- // parse_var_function() / parse_env_function() ensure neither .unwrap() will fail.
- let name = {
- let name = input.expect_ident().unwrap();
- if is_env {
- Atom::from(&**name)
- } else {
- Atom::from(parse_name(&name).unwrap())
- }
- };
-
- let env_value;
- let value = if is_env {
- if let Some(v) = device.environment().get(&name, device) {
- env_value = v;
- Some(&env_value)
- } else {
- None
- }
- } else {
- custom_properties.get(&name).map(|v| &**v)
- };
-
- if let Some(v) = value {
- last_token_type = v.last_token_type;
- partial_computed_value.push_variable(input, v)?;
- // Skip over the fallback, as `parse_nested_block` would return `Err`
- // if we don't consume all of `input`.
- // FIXME: Add a specialized method to cssparser to do this with less work.
- while input.next().is_ok() {}
- } else {
- input.expect_comma()?;
- input.skip_whitespace();
- let after_comma = input.state();
- let first_token_type = input
- .next_including_whitespace_and_comments()
- .ok()
- .map_or_else(TokenSerializationType::nothing, |t| {
- t.serialization_type()
- });
- input.reset(&after_comma);
- let mut position = (after_comma.position(), first_token_type);
- last_token_type = substitute_block(
- input,
- &mut position,
- partial_computed_value,
- custom_properties,
- device,
- )?;
- partial_computed_value.push_from(input, position, last_token_type)?;
- }
- Ok(())
- })?;
- set_position_at_next_iteration = true
- },
- Token::Function(_) |
- Token::ParenthesisBlock |
- Token::CurlyBracketBlock |
- Token::SquareBracketBlock => {
- input.parse_nested_block(|input| {
- substitute_block(
- input,
- position,
- partial_computed_value,
- custom_properties,
- device,
- )
- })?;
- // It's the same type for CloseCurlyBracket and CloseSquareBracket.
- last_token_type = Token::CloseParenthesis.serialization_type();
- },
-
- _ => last_token_type = token.serialization_type(),
- }
- }
- // FIXME: deal with things being implicitly closed at the end of the input. E.g.
- // ```html
- // <div style="--color: rgb(0,0,0">
- // <p style="background: var(--color) var(--image) top left; --image: url('a.png"></p>
- // </div>
- // ```
- Ok(last_token_type)
-}
-
-/// Replace `var()` and `env()` functions for a non-custom property.
-///
-/// Return `Err(())` for invalid at computed time.
-pub fn substitute<'i>(
- input: &'i str,
- first_token_type: TokenSerializationType,
- computed_values_map: Option<&Arc<CustomPropertiesMap>>,
- device: &Device,
-) -> Result<String, ParseError<'i>> {
- let mut substituted = ComputedValue::empty();
- let mut input = ParserInput::new(input);
- let mut input = Parser::new(&mut input);
- let mut position = (input.position(), first_token_type);
- let empty_map = CustomPropertiesMap::default();
- let custom_properties = match computed_values_map {
- Some(m) => &**m,
- None => &empty_map,
- };
- let last_token_type = substitute_block(
- &mut input,
- &mut position,
- &mut substituted,
- &custom_properties,
- device,
- )?;
- substituted.push_from(&input, position, last_token_type)?;
- Ok(substituted.css)
-}
diff --git a/components/style/data.rs b/components/style/data.rs
deleted file mode 100644
index 62dff225f8f..00000000000
--- a/components/style/data.rs
+++ /dev/null
@@ -1,545 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
-
-//! Per-node data used in style calculation.
-
-use crate::computed_value_flags::ComputedValueFlags;
-use crate::context::{SharedStyleContext, StackLimitChecker};
-use crate::dom::TElement;
-use crate::invalidation::element::invalidator::InvalidationResult;
-use crate::invalidation::element::restyle_hints::RestyleHint;
-use crate::properties::ComputedValues;
-use crate::selector_parser::{PseudoElement, RestyleDamage, EAGER_PSEUDO_COUNT};
-use crate::style_resolver::{PrimaryStyle, ResolvedElementStyles, ResolvedStyle};
-#[cfg(feature = "gecko")]
-use malloc_size_of::MallocSizeOfOps;
-use selectors::NthIndexCache;
-use servo_arc::Arc;
-use std::fmt;
-use std::mem;
-use std::ops::{Deref, DerefMut};
-
-bitflags! {
- /// Various flags stored on ElementData.
- #[derive(Default)]
- pub struct ElementDataFlags: u8 {
- /// Whether the styles changed for this restyle.
- const WAS_RESTYLED = 1 << 0;
- /// Whether the last traversal of this element did not do
- /// any style computation. This is not true during the initial
- /// styling pass, nor is it true when we restyle (in which case
- /// WAS_RESTYLED is set).
- ///
- /// This bit always corresponds to the last time the element was
- /// traversed, so each traversal simply updates it with the appropriate
- /// value.
- const TRAVERSED_WITHOUT_STYLING = 1 << 1;
-
- /// Whether the primary style of this element data was reused from
- /// another element via a rule node comparison. This allows us to
- /// differentiate between elements that shared styles because they met
- /// all the criteria of the style sharing cache, compared to elements
- /// that reused style structs via rule node identity.
- ///
- /// The former gives us stronger transitive guarantees that allows us to
- /// apply the style sharing cache to cousins.
- const PRIMARY_STYLE_REUSED_VIA_RULE_NODE = 1 << 2;
- }
-}
-
-/// A lazily-allocated list of styles for eagerly-cascaded pseudo-elements.
-///
-/// We use an Arc so that sharing these styles via the style sharing cache does
-/// not require duplicate allocations. We leverage the copy-on-write semantics of
-/// Arc::make_mut(), which is free (i.e. does not require atomic RMU operations)
-/// in servo_arc.
-#[derive(Clone, Debug, Default)]
-pub struct EagerPseudoStyles(Option<Arc<EagerPseudoArray>>);
-
-#[derive(Default)]
-struct EagerPseudoArray(EagerPseudoArrayInner);
-type EagerPseudoArrayInner = [Option<Arc<ComputedValues>>; EAGER_PSEUDO_COUNT];
-
-impl Deref for EagerPseudoArray {
- type Target = EagerPseudoArrayInner;
- fn deref(&self) -> &Self::Target {
- &self.0
- }
-}
-
-impl DerefMut for EagerPseudoArray {
- fn deref_mut(&mut self) -> &mut Self::Target {
- &mut self.0
- }
-}
-
-// Manually implement `Clone` here because the derived impl of `Clone` for
-// array types assumes the value inside is `Copy`.
-impl Clone for EagerPseudoArray {
- fn clone(&self) -> Self {
- let mut clone = Self::default();
- for i in 0..EAGER_PSEUDO_COUNT {
- clone[i] = self.0[i].clone();
- }
- clone
- }
-}
-
-// Override Debug to print which pseudos we have, and substitute the rule node
-// for the much-more-verbose ComputedValues stringification.
-impl fmt::Debug for EagerPseudoArray {
- fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
- write!(f, "EagerPseudoArray {{ ")?;
- for i in 0..EAGER_PSEUDO_COUNT {
- if let Some(ref values) = self[i] {
- write!(
- f,
- "{:?}: {:?}, ",
- PseudoElement::from_eager_index(i),
- &values.rules
- )?;
- }
- }
- write!(f, "}}")
- }
-}
-
-// Can't use [None; EAGER_PSEUDO_COUNT] here because it complains
-// about Copy not being implemented for our Arc type.
-#[cfg(feature = "gecko")]
-const EMPTY_PSEUDO_ARRAY: &'static EagerPseudoArrayInner = &[None, None, None, None];
-#[cfg(feature = "servo")]
-const EMPTY_PSEUDO_ARRAY: &'static EagerPseudoArrayInner = &[None, None, None];
-
-impl EagerPseudoStyles {
- /// Returns whether there are any pseudo styles.
- pub fn is_empty(&self) -> bool {
- self.0.is_none()
- }
-
- /// Grabs a reference to the list of styles, if they exist.
- pub fn as_optional_array(&self) -> Option<&EagerPseudoArrayInner> {
- match self.0 {
- None => None,
- Some(ref x) => Some(&x.0),
- }
- }
-
- /// Grabs a reference to the list of styles or a list of None if
- /// there are no styles to be had.
- pub fn as_array(&self) -> &EagerPseudoArrayInner {
- self.as_optional_array().unwrap_or(EMPTY_PSEUDO_ARRAY)
- }
-
- /// Returns a reference to the style for a given eager pseudo, if it exists.
- pub fn get(&self, pseudo: &PseudoElement) -> Option<&Arc<ComputedValues>> {
- debug_assert!(pseudo.is_eager());
- self.0
- .as_ref()
- .and_then(|p| p[pseudo.eager_index()].as_ref())
- }
-
- /// Sets the style for the eager pseudo.
- pub fn set(&mut self, pseudo: &PseudoElement, value: Arc<ComputedValues>) {
- if self.0.is_none() {
- self.0 = Some(Arc::new(Default::default()));
- }
- let arr = Arc::make_mut(self.0.as_mut().unwrap());
- arr[pseudo.eager_index()] = Some(value);
- }
-}
-
-/// The styles associated with a node, including the styles for any
-/// pseudo-elements.
-#[derive(Clone, Default)]
-pub struct ElementStyles {
- /// The element's style.
- pub primary: Option<Arc<ComputedValues>>,
- /// A list of the styles for the element's eagerly-cascaded pseudo-elements.
- pub pseudos: EagerPseudoStyles,
-}
-
-// There's one of these per rendered elements so it better be small.
-size_of_test!(ElementStyles, 16);
-
-/// Information on how this element uses viewport units.
-#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)]
-pub enum ViewportUnitUsage {
- /// No viewport units are used.
- None = 0,
- /// There are viewport units used from regular style rules (which means we
- /// should re-cascade).
- FromDeclaration,
- /// There are viewport units used from container queries (which means we
- /// need to re-selector-match).
- FromQuery,
-}
-
-impl ElementStyles {
- /// Returns the primary style.
- pub fn get_primary(&self) -> Option<&Arc<ComputedValues>> {
- self.primary.as_ref()
- }
-
- /// Returns the primary style. Panic if no style available.
- pub fn primary(&self) -> &Arc<ComputedValues> {
- self.primary.as_ref().unwrap()
- }
-
- /// Whether this element `display` value is `none`.
- pub fn is_display_none(&self) -> bool {
- self.primary().get_box().clone_display().is_none()
- }
-
- /// Whether this element uses viewport units.
- pub fn viewport_unit_usage(&self) -> ViewportUnitUsage {
- fn usage_from_flags(flags: ComputedValueFlags) -> ViewportUnitUsage {
- if flags.intersects(ComputedValueFlags::USES_VIEWPORT_UNITS_ON_CONTAINER_QUERIES) {
- return ViewportUnitUsage::FromQuery;
- }
- if flags.intersects(ComputedValueFlags::USES_VIEWPORT_UNITS) {
- return ViewportUnitUsage::FromDeclaration;
- }
- ViewportUnitUsage::None
- }
-
- let mut usage = usage_from_flags(self.primary().flags);
- for pseudo_style in self.pseudos.as_array() {
- if let Some(ref pseudo_style) = pseudo_style {
- usage = std::cmp::max(usage, usage_from_flags(pseudo_style.flags));
- }
- }
-
- usage
- }
-
- #[cfg(feature = "gecko")]
- fn size_of_excluding_cvs(&self, _ops: &mut MallocSizeOfOps) -> usize {
- // As the method name suggests, we don't measures the ComputedValues
- // here, because they are measured on the C++ side.
-
- // XXX: measure the EagerPseudoArray itself, but not the ComputedValues
- // within it.
-
- 0
- }
-}
-
-// We manually implement Debug for ElementStyles so that we can avoid the
-// verbose stringification of every property in the ComputedValues. We
-// substitute the rule node instead.
-impl fmt::Debug for ElementStyles {
- fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
- write!(
- f,
- "ElementStyles {{ primary: {:?}, pseudos: {:?} }}",
- self.primary.as_ref().map(|x| &x.rules),
- self.pseudos
- )
- }
-}
-
-/// Style system data associated with an Element.
-///
-/// In Gecko, this hangs directly off the Element. Servo, this is embedded
-/// inside of layout data, which itself hangs directly off the Element. In
-/// both cases, it is wrapped inside an AtomicRefCell to ensure thread safety.
-#[derive(Debug, Default)]
-pub struct ElementData {
- /// The styles for the element and its pseudo-elements.
- pub styles: ElementStyles,
-
- /// The restyle damage, indicating what kind of layout changes are required
- /// afte restyling.
- pub damage: RestyleDamage,
-
- /// The restyle hint, which indicates whether selectors need to be rematched
- /// for this element, its children, and its descendants.
- pub hint: RestyleHint,
-
- /// Flags.
- pub flags: ElementDataFlags,
-}
-
-// There's one of these per rendered elements so it better be small.
-size_of_test!(ElementData, 24);
-
-/// The kind of restyle that a single element should do.
-#[derive(Debug)]
-pub enum RestyleKind {
- /// We need to run selector matching plus re-cascade, that is, a full
- /// restyle.
- MatchAndCascade,
- /// We need to recascade with some replacement rule, such as the style
- /// attribute, or animation rules.
- CascadeWithReplacements(RestyleHint),
- /// We only need to recascade, for example, because only inherited
- /// properties in the parent changed.
- CascadeOnly,
-}
-
-impl ElementData {
- /// Invalidates style for this element, its descendants, and later siblings,
- /// based on the snapshot of the element that we took when attributes or
- /// state changed.
- pub fn invalidate_style_if_needed<'a, E: TElement>(
- &mut self,
- element: E,
- shared_context: &SharedStyleContext,
- stack_limit_checker: Option<&StackLimitChecker>,
- nth_index_cache: &mut NthIndexCache,
- ) -> InvalidationResult {
- // In animation-only restyle we shouldn't touch snapshot at all.
- if shared_context.traversal_flags.for_animation_only() {
- return InvalidationResult::empty();
- }
-
- use crate::invalidation::element::invalidator::TreeStyleInvalidator;
- use crate::invalidation::element::state_and_attributes::StateAndAttrInvalidationProcessor;
-
- debug!(
- "invalidate_style_if_needed: {:?}, flags: {:?}, has_snapshot: {}, \
- handled_snapshot: {}, pseudo: {:?}",
- element,
- shared_context.traversal_flags,
- element.has_snapshot(),
- element.handled_snapshot(),
- element.implemented_pseudo_element()
- );
-
- if !element.has_snapshot() || element.handled_snapshot() {
- return InvalidationResult::empty();
- }
-
- let mut processor =
- StateAndAttrInvalidationProcessor::new(shared_context, element, self, nth_index_cache);
-
- let invalidator = TreeStyleInvalidator::new(element, stack_limit_checker, &mut processor);
-
- let result = invalidator.invalidate();
-
- unsafe { element.set_handled_snapshot() }
- debug_assert!(element.handled_snapshot());
-
- result
- }
-
- /// Returns true if this element has styles.
- #[inline]
- pub fn has_styles(&self) -> bool {
- self.styles.primary.is_some()
- }
-
- /// Returns this element's styles as resolved styles to use for sharing.
- pub fn share_styles(&self) -> ResolvedElementStyles {
- ResolvedElementStyles {
- primary: self.share_primary_style(),
- pseudos: self.styles.pseudos.clone(),
- }
- }
-
- /// Returns this element's primary style as a resolved style to use for sharing.
- pub fn share_primary_style(&self) -> PrimaryStyle {
- let reused_via_rule_node = self
- .flags
- .contains(ElementDataFlags::PRIMARY_STYLE_REUSED_VIA_RULE_NODE);
-
- PrimaryStyle {
- style: ResolvedStyle(self.styles.primary().clone()),
- reused_via_rule_node,
- }
- }
-
- /// Sets a new set of styles, returning the old ones.
- pub fn set_styles(&mut self, new_styles: ResolvedElementStyles) -> ElementStyles {
- if new_styles.primary.reused_via_rule_node {
- self.flags
- .insert(ElementDataFlags::PRIMARY_STYLE_REUSED_VIA_RULE_NODE);
- } else {
- self.flags
- .remove(ElementDataFlags::PRIMARY_STYLE_REUSED_VIA_RULE_NODE);
- }
- mem::replace(&mut self.styles, new_styles.into())
- }
-
- /// Returns the kind of restyling that we're going to need to do on this
- /// element, based of the stored restyle hint.
- pub fn restyle_kind(&self, shared_context: &SharedStyleContext) -> Option<RestyleKind> {
- if shared_context.traversal_flags.for_animation_only() {
- return self.restyle_kind_for_animation(shared_context);
- }
-
- let style = match self.styles.primary {
- Some(ref s) => s,
- None => return Some(RestyleKind::MatchAndCascade),
- };
-
- let hint = self.hint;
- if hint.is_empty() {
- return None;
- }
-
- let needs_to_match_self = hint.intersects(RestyleHint::RESTYLE_SELF) ||
- (hint.intersects(RestyleHint::RESTYLE_SELF_IF_PSEUDO) && style.is_pseudo_style());
- if needs_to_match_self {
- return Some(RestyleKind::MatchAndCascade);
- }
-
- if hint.has_replacements() {
- debug_assert!(
- !hint.has_animation_hint(),
- "Animation only restyle hint should have already processed"
- );
- return Some(RestyleKind::CascadeWithReplacements(
- hint & RestyleHint::replacements(),
- ));
- }
-
- let needs_to_recascade_self = hint.intersects(RestyleHint::RECASCADE_SELF) ||
- (hint.intersects(RestyleHint::RECASCADE_SELF_IF_INHERIT_RESET_STYLE) &&
- style
- .flags
- .contains(ComputedValueFlags::INHERITS_RESET_STYLE));
- if needs_to_recascade_self {
- return Some(RestyleKind::CascadeOnly);
- }
-
- None
- }
-
- /// Returns the kind of restyling for animation-only restyle.
- fn restyle_kind_for_animation(
- &self,
- shared_context: &SharedStyleContext,
- ) -> Option<RestyleKind> {
- debug_assert!(shared_context.traversal_flags.for_animation_only());
- debug_assert!(
- self.has_styles(),
- "animation traversal doesn't care about unstyled elements"
- );
-
- // FIXME: We should ideally restyle here, but it is a hack to work around our weird
- // animation-only traversal stuff: If we're display: none and the rules we could
- // match could change, we consider our style up-to-date. This is because re-cascading with
- // and old style doesn't guarantee returning the correct animation style (that's
- // bug 1393323). So if our display changed, and it changed from display: none, we would
- // incorrectly forget about it and wouldn't be able to correctly style our descendants
- // later.
- // XXX Figure out if this still makes sense.
- let hint = self.hint;
- if self.styles.is_display_none() && hint.intersects(RestyleHint::RESTYLE_SELF) {
- return None;
- }
-
- let style = self.styles.primary();
- // Return either CascadeWithReplacements or CascadeOnly in case of
- // animation-only restyle. I.e. animation-only restyle never does
- // selector matching.
- if hint.has_animation_hint() {
- return Some(RestyleKind::CascadeWithReplacements(
- hint & RestyleHint::for_animations(),
- ));
- }
-
- let needs_to_recascade_self = hint.intersects(RestyleHint::RECASCADE_SELF) ||
- (hint.intersects(RestyleHint::RECASCADE_SELF_IF_INHERIT_RESET_STYLE) &&
- style
- .flags
- .contains(ComputedValueFlags::INHERITS_RESET_STYLE));
- if needs_to_recascade_self {
- return Some(RestyleKind::CascadeOnly);
- }
- return None;
- }
-
- /// Drops any restyle state from the element.
- ///
- /// FIXME(bholley): The only caller of this should probably just assert that
- /// the hint is empty and call clear_flags_and_damage().
- #[inline]
- pub fn clear_restyle_state(&mut self) {
- self.hint = RestyleHint::empty();
- self.clear_restyle_flags_and_damage();
- }
-
- /// Drops restyle flags and damage from the element.
- #[inline]
- pub fn clear_restyle_flags_and_damage(&mut self) {
- self.damage = RestyleDamage::empty();
- self.flags.remove(ElementDataFlags::WAS_RESTYLED);
- }
-
- /// Mark this element as restyled, which is useful to know whether we need
- /// to do a post-traversal.
- pub fn set_restyled(&mut self) {
- self.flags.insert(ElementDataFlags::WAS_RESTYLED);
- self.flags
- .remove(ElementDataFlags::TRAVERSED_WITHOUT_STYLING);
- }
-
- /// Returns true if this element was restyled.
- #[inline]
- pub fn is_restyle(&self) -> bool {
- self.flags.contains(ElementDataFlags::WAS_RESTYLED)
- }
-
- /// Mark that we traversed this element without computing any style for it.
- pub fn set_traversed_without_styling(&mut self) {
- self.flags
- .insert(ElementDataFlags::TRAVERSED_WITHOUT_STYLING);
- }
-
- /// Returns whether this element has been part of a restyle.
- #[inline]
- pub fn contains_restyle_data(&self) -> bool {
- self.is_restyle() || !self.hint.is_empty() || !self.damage.is_empty()
- }
-
- /// Returns whether it is safe to perform cousin sharing based on the ComputedValues
- /// identity of the primary style in this ElementData. There are a few subtle things
- /// to check.
- ///
- /// First, if a parent element was already styled and we traversed past it without
- /// restyling it, that may be because our clever invalidation logic was able to prove
- /// that the styles of that element would remain unchanged despite changes to the id
- /// or class attributes. However, style sharing relies on the strong guarantee that all
- /// the classes and ids up the respective parent chains are identical. As such, if we
- /// skipped styling for one (or both) of the parents on this traversal, we can't share
- /// styles across cousins. Note that this is a somewhat conservative check. We could
- /// tighten it by having the invalidation logic explicitly flag elements for which it
- /// ellided styling.
- ///
- /// Second, we want to only consider elements whose ComputedValues match due to a hit
- /// in the style sharing cache, rather than due to the rule-node-based reuse that
- /// happens later in the styling pipeline. The former gives us the stronger guarantees
- /// we need for style sharing, the latter does not.
- pub fn safe_for_cousin_sharing(&self) -> bool {
- if self.flags.intersects(
- ElementDataFlags::TRAVERSED_WITHOUT_STYLING |
- ElementDataFlags::PRIMARY_STYLE_REUSED_VIA_RULE_NODE,
- ) {
- return false;
- }
- if !self
- .styles
- .primary()
- .get_box()
- .clone_container_type()
- .is_normal()
- {
- return false;
- }
- true
- }
-
- /// Measures memory usage.
- #[cfg(feature = "gecko")]
- pub fn size_of_excluding_cvs(&self, ops: &mut MallocSizeOfOps) -> usize {
- let n = self.styles.size_of_excluding_cvs(ops);
-
- // We may measure more fields in the future if DMD says it's worth it.
-
- n
- }
-}
diff --git a/components/style/dom.rs b/components/style/dom.rs
deleted file mode 100644
index 7fc29280e69..00000000000
--- a/components/style/dom.rs
+++ /dev/null
@@ -1,940 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
-
-//! Types and traits used to access the DOM from style calculation.
-
-#![allow(unsafe_code)]
-#![deny(missing_docs)]
-
-use crate::applicable_declarations::ApplicableDeclarationBlock;
-use crate::context::SharedStyleContext;
-#[cfg(feature = "gecko")]
-use crate::context::{PostAnimationTasks, UpdateAnimationsTasks};
-use crate::data::ElementData;
-use crate::media_queries::Device;
-use crate::properties::{AnimationDeclarations, ComputedValues, PropertyDeclarationBlock};
-use crate::selector_parser::{AttrValue, Lang, PseudoElement, SelectorImpl};
-use crate::shared_lock::{Locked, SharedRwLock};
-use crate::stylist::CascadeData;
-use crate::values::computed::Display;
-use crate::values::AtomIdent;
-use crate::{LocalName, WeakAtom};
-use atomic_refcell::{AtomicRef, AtomicRefMut};
-use selectors::matching::{QuirksMode, VisitedHandlingMode};
-use selectors::sink::Push;
-use selectors::Element as SelectorsElement;
-use servo_arc::{Arc, ArcBorrow};
-use std::fmt;
-use std::fmt::Debug;
-use std::hash::Hash;
-use std::ops::Deref;
-use style_traits::dom::ElementState;
-
-pub use style_traits::dom::OpaqueNode;
-
-/// Simple trait to provide basic information about the type of an element.
-///
-/// We avoid exposing the full type id, since computing it in the general case
-/// would be difficult for Gecko nodes.
-pub trait NodeInfo {
- /// Whether this node is an element.
- fn is_element(&self) -> bool;
- /// Whether this node is a text node.
- fn is_text_node(&self) -> bool;
-}
-
-/// A node iterator that only returns node that don't need layout.
-pub struct LayoutIterator<T>(pub T);
-
-impl<T, N> Iterator for LayoutIterator<T>
-where
- T: Iterator<Item = N>,
- N: NodeInfo,
-{
- type Item = N;
-
- fn next(&mut self) -> Option<N> {
- loop {
- let n = self.0.next()?;
- // Filter out nodes that layout should ignore.
- if n.is_text_node() || n.is_element() {
- return Some(n);
- }
- }
- }
-}
-
-/// An iterator over the DOM children of a node.
-pub struct DomChildren<N>(Option<N>);
-impl<N> Iterator for DomChildren<N>
-where
- N: TNode,
-{
- type Item = N;
-
- fn next(&mut self) -> Option<N> {
- let n = self.0.take()?;
- self.0 = n.next_sibling();
- Some(n)
- }
-}
-
-/// An iterator over the DOM descendants of a node in pre-order.
-pub struct DomDescendants<N> {
- previous: Option<N>,
- scope: N,
-}
-
-impl<N> Iterator for DomDescendants<N>
-where
- N: TNode,
-{
- type Item = N;
-
- #[inline]
- fn next(&mut self) -> Option<N> {
- let prev = self.previous.take()?;
- self.previous = prev.next_in_preorder(self.scope);
- self.previous
- }
-}
-
-/// The `TDocument` trait, to represent a document node.
-pub trait TDocument: Sized + Copy + Clone {
- /// The concrete `TNode` type.
- type ConcreteNode: TNode<ConcreteDocument = Self>;
-
- /// Get this document as a `TNode`.
- fn as_node(&self) -> Self::ConcreteNode;
-
- /// Returns whether this document is an HTML document.
- fn is_html_document(&self) -> bool;
-
- /// Returns the quirks mode of this document.
- fn quirks_mode(&self) -> QuirksMode;
-
- /// Get a list of elements with a given ID in this document, sorted by
- /// tree position.
- ///
- /// Can return an error to signal that this list is not available, or also
- /// return an empty slice.
- fn elements_with_id<'a>(
- &self,
- _id: &AtomIdent,
- ) -> Result<&'a [<Self::ConcreteNode as TNode>::ConcreteElement], ()>
- where
- Self: 'a,
- {
- Err(())
- }
-
- /// This document's shared lock.
- fn shared_lock(&self) -> &SharedRwLock;
-}
-
-/// The `TNode` trait. This is the main generic trait over which the style
-/// system can be implemented.
-pub trait TNode: Sized + Copy + Clone + Debug + NodeInfo + PartialEq {
- /// The concrete `TElement` type.
- type ConcreteElement: TElement<ConcreteNode = Self>;
-
- /// The concrete `TDocument` type.
- type ConcreteDocument: TDocument<ConcreteNode = Self>;
-
- /// The concrete `TShadowRoot` type.
- type ConcreteShadowRoot: TShadowRoot<ConcreteNode = Self>;
-
- /// Get this node's parent node.
- fn parent_node(&self) -> Option<Self>;
-
- /// Get this node's first child.
- fn first_child(&self) -> Option<Self>;
-
- /// Get this node's last child.
- fn last_child(&self) -> Option<Self>;
-
- /// Get this node's previous sibling.
- fn prev_sibling(&self) -> Option<Self>;
-
- /// Get this node's next sibling.
- fn next_sibling(&self) -> Option<Self>;
-
- /// Get the owner document of this node.
- fn owner_doc(&self) -> Self::ConcreteDocument;
-
- /// Iterate over the DOM children of a node.
- #[inline(always)]
- fn dom_children(&self) -> DomChildren<Self> {
- DomChildren(self.first_child())
- }
-
- /// Returns whether the node is attached to a document.
- fn is_in_document(&self) -> bool;
-
- /// Iterate over the DOM children of a node, in preorder.
- #[inline(always)]
- fn dom_descendants(&self) -> DomDescendants<Self> {
- DomDescendants {
- previous: Some(*self),
- scope: *self,
- }
- }
-
- /// Returns the next node after this one, in a pre-order tree-traversal of
- /// the subtree rooted at scoped_to.
- #[inline]
- fn next_in_preorder(&self, scoped_to: Self) -> Option<Self> {
- if let Some(c) = self.first_child() {
- return Some(c);
- }
-
- let mut current = *self;
- loop {
- if current == scoped_to {
- return None;
- }
-
- if let Some(s) = current.next_sibling() {
- return Some(s);
- }
-
- debug_assert!(
- current.parent_node().is_some(),
- "Not a descendant of the scope?"
- );
- current = current.parent_node()?;
- }
- }
-
- /// Get this node's parent element from the perspective of a restyle
- /// traversal.
- fn traversal_parent(&self) -> Option<Self::ConcreteElement>;
-
- /// Get this node's parent element if present.
- fn parent_element(&self) -> Option<Self::ConcreteElement> {
- self.parent_node().and_then(|n| n.as_element())
- }
-
- /// Get this node's parent element, or shadow host if it's a shadow root.
- fn parent_element_or_host(&self) -> Option<Self::ConcreteElement> {
- let parent = self.parent_node()?;
- if let Some(e) = parent.as_element() {
- return Some(e);
- }
- if let Some(root) = parent.as_shadow_root() {
- return Some(root.host());
- }
- None
- }
-
- /// Converts self into an `OpaqueNode`.
- fn opaque(&self) -> OpaqueNode;
-
- /// A debug id, only useful, mm... for debugging.
- fn debug_id(self) -> usize;
-
- /// Get this node as an element, if it's one.
- fn as_element(&self) -> Option<Self::ConcreteElement>;
-
- /// Get this node as a document, if it's one.
- fn as_document(&self) -> Option<Self::ConcreteDocument>;
-
- /// Get this node as a ShadowRoot, if it's one.
- fn as_shadow_root(&self) -> Option<Self::ConcreteShadowRoot>;
-}
-
-/// Wrapper to output the subtree rather than the single node when formatting
-/// for Debug.
-pub struct ShowSubtree<N: TNode>(pub N);
-impl<N: TNode> Debug for ShowSubtree<N> {
- fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
- writeln!(f, "DOM Subtree:")?;
- fmt_subtree(f, &|f, n| write!(f, "{:?}", n), self.0, 1)
- }
-}
-
-/// Wrapper to output the subtree along with the ElementData when formatting
-/// for Debug.
-pub struct ShowSubtreeData<N: TNode>(pub N);
-impl<N: TNode> Debug for ShowSubtreeData<N> {
- fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
- writeln!(f, "DOM Subtree:")?;
- fmt_subtree(f, &|f, n| fmt_with_data(f, n), self.0, 1)
- }
-}
-
-/// Wrapper to output the subtree along with the ElementData and primary
-/// ComputedValues when formatting for Debug. This is extremely verbose.
-#[cfg(feature = "servo")]
-pub struct ShowSubtreeDataAndPrimaryValues<N: TNode>(pub N);
-#[cfg(feature = "servo")]
-impl<N: TNode> Debug for ShowSubtreeDataAndPrimaryValues<N> {
- fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
- writeln!(f, "DOM Subtree:")?;
- fmt_subtree(f, &|f, n| fmt_with_data_and_primary_values(f, n), self.0, 1)
- }
-}
-
-fn fmt_with_data<N: TNode>(f: &mut fmt::Formatter, n: N) -> fmt::Result {
- if let Some(el) = n.as_element() {
- write!(
- f,
- "{:?} dd={} aodd={} data={:?}",
- el,
- el.has_dirty_descendants(),
- el.has_animation_only_dirty_descendants(),
- el.borrow_data(),
- )
- } else {
- write!(f, "{:?}", n)
- }
-}
-
-#[cfg(feature = "servo")]
-fn fmt_with_data_and_primary_values<N: TNode>(f: &mut fmt::Formatter, n: N) -> fmt::Result {
- if let Some(el) = n.as_element() {
- let dd = el.has_dirty_descendants();
- let aodd = el.has_animation_only_dirty_descendants();
- let data = el.borrow_data();
- let values = data.as_ref().and_then(|d| d.styles.get_primary());
- write!(
- f,
- "{:?} dd={} aodd={} data={:?} values={:?}",
- el, dd, aodd, &data, values
- )
- } else {
- write!(f, "{:?}", n)
- }
-}
-
-fn fmt_subtree<F, N: TNode>(f: &mut fmt::Formatter, stringify: &F, n: N, indent: u32) -> fmt::Result
-where
- F: Fn(&mut fmt::Formatter, N) -> fmt::Result,
-{
- for _ in 0..indent {
- write!(f, " ")?;
- }
- stringify(f, n)?;
- if let Some(e) = n.as_element() {
- for kid in e.traversal_children() {
- writeln!(f, "")?;
- fmt_subtree(f, stringify, kid, indent + 1)?;
- }
- }
-
- Ok(())
-}
-
-/// The ShadowRoot trait.
-pub trait TShadowRoot: Sized + Copy + Clone + Debug + PartialEq {
- /// The concrete node type.
- type ConcreteNode: TNode<ConcreteShadowRoot = Self>;
-
- /// Get this ShadowRoot as a node.
- fn as_node(&self) -> Self::ConcreteNode;
-
- /// Get the shadow host that hosts this ShadowRoot.
- fn host(&self) -> <Self::ConcreteNode as TNode>::ConcreteElement;
-
- /// Get the style data for this ShadowRoot.
- fn style_data<'a>(&self) -> Option<&'a CascadeData>
- where
- Self: 'a;
-
- /// Get the list of shadow parts for this shadow root.
- fn parts<'a>(&self) -> &[<Self::ConcreteNode as TNode>::ConcreteElement]
- where
- Self: 'a,
- {
- &[]
- }
-
- /// Get a list of elements with a given ID in this shadow root, sorted by
- /// tree position.
- ///
- /// Can return an error to signal that this list is not available, or also
- /// return an empty slice.
- fn elements_with_id<'a>(
- &self,
- _id: &AtomIdent,
- ) -> Result<&'a [<Self::ConcreteNode as TNode>::ConcreteElement], ()>
- where
- Self: 'a,
- {
- Err(())
- }
-}
-
-/// The element trait, the main abstraction the style crate acts over.
-pub trait TElement:
- Eq + PartialEq + Debug + Hash + Sized + Copy + Clone + SelectorsElement<Impl = SelectorImpl>
-{
- /// The concrete node type.
- type ConcreteNode: TNode<ConcreteElement = Self>;
-
- /// A concrete children iterator type in order to iterate over the `Node`s.
- ///
- /// TODO(emilio): We should eventually replace this with the `impl Trait`
- /// syntax.
- type TraversalChildrenIterator: Iterator<Item = Self::ConcreteNode>;
-
- /// Get this element as a node.
- fn as_node(&self) -> Self::ConcreteNode;
-
- /// A debug-only check that the device's owner doc matches the actual doc
- /// we're the root of.
- ///
- /// Otherwise we may set document-level state incorrectly, like the root
- /// font-size used for rem units.
- fn owner_doc_matches_for_testing(&self, _: &Device) -> bool {
- true
- }
-
- /// Whether this element should match user and content rules.
- ///
- /// We use this for Native Anonymous Content in Gecko.
- fn matches_user_and_content_rules(&self) -> bool {
- true
- }
-
- /// Returns the depth of this element in the DOM.
- fn depth(&self) -> usize {
- let mut depth = 0;
- let mut curr = *self;
- while let Some(parent) = curr.traversal_parent() {
- depth += 1;
- curr = parent;
- }
-
- depth
- }
-
- /// Get this node's parent element from the perspective of a restyle
- /// traversal.
- fn traversal_parent(&self) -> Option<Self> {
- self.as_node().traversal_parent()
- }
-
- /// Get this node's children from the perspective of a restyle traversal.
- fn traversal_children(&self) -> LayoutIterator<Self::TraversalChildrenIterator>;
-
- /// Returns the parent element we should inherit from.
- ///
- /// This is pretty much always the parent element itself, except in the case
- /// of Gecko's Native Anonymous Content, which uses the traversal parent
- /// (i.e. the flattened tree parent) and which also may need to find the
- /// closest non-NAC ancestor.
- fn inheritance_parent(&self) -> Option<Self> {
- self.parent_element()
- }
-
- /// The ::before pseudo-element of this element, if it exists.
- fn before_pseudo_element(&self) -> Option<Self> {
- None
- }
-
- /// The ::after pseudo-element of this element, if it exists.
- fn after_pseudo_element(&self) -> Option<Self> {
- None
- }
-
- /// The ::marker pseudo-element of this element, if it exists.
- fn marker_pseudo_element(&self) -> Option<Self> {
- None
- }
-
- /// Execute `f` for each anonymous content child (apart from ::before and
- /// ::after) whose originating element is `self`.
- fn each_anonymous_content_child<F>(&self, _f: F)
- where
- F: FnMut(Self),
- {
- }
-
- /// Return whether this element is an element in the HTML namespace.
- fn is_html_element(&self) -> bool;
-
- /// Return whether this element is an element in the MathML namespace.
- fn is_mathml_element(&self) -> bool;
-
- /// Return whether this element is an element in the SVG namespace.
- fn is_svg_element(&self) -> bool;
-
- /// Return whether this element is an element in the XUL namespace.
- fn is_xul_element(&self) -> bool {
- false
- }
-
- /// Return the list of slotted nodes of this node.
- fn slotted_nodes(&self) -> &[Self::ConcreteNode] {
- &[]
- }
-
- /// Get this element's style attribute.
- fn style_attribute(&self) -> Option<ArcBorrow<Locked<PropertyDeclarationBlock>>>;
-
- /// Unset the style attribute's dirty bit.
- /// Servo doesn't need to manage ditry bit for style attribute.
- fn unset_dirty_style_attribute(&self) {}
-
- /// Get this element's SMIL override declarations.
- fn smil_override(&self) -> Option<ArcBorrow<Locked<PropertyDeclarationBlock>>> {
- None
- }
-
- /// Get the combined animation and transition rules.
- ///
- /// FIXME(emilio): Is this really useful?
- fn animation_declarations(&self, context: &SharedStyleContext) -> AnimationDeclarations {
- if !self.may_have_animations() {
- return Default::default();
- }
-
- AnimationDeclarations {
- animations: self.animation_rule(context),
- transitions: self.transition_rule(context),
- }
- }
-
- /// Get this element's animation rule.
- fn animation_rule(
- &self,
- _: &SharedStyleContext,
- ) -> Option<Arc<Locked<PropertyDeclarationBlock>>>;
-
- /// Get this element's transition rule.
- fn transition_rule(
- &self,
- context: &SharedStyleContext,
- ) -> Option<Arc<Locked<PropertyDeclarationBlock>>>;
-
- /// Get this element's state, for non-tree-structural pseudos.
- fn state(&self) -> ElementState;
-
- /// Returns whether this element has a `part` attribute.
- fn has_part_attr(&self) -> bool;
-
- /// Returns whether this element exports any part from its shadow tree.
- fn exports_any_part(&self) -> bool;
-
- /// The ID for this element.
- fn id(&self) -> Option<&WeakAtom>;
-
- /// Internal iterator for the classes of this element.
- fn each_class<F>(&self, callback: F)
- where
- F: FnMut(&AtomIdent);
-
- /// Internal iterator for the part names of this element.
- fn each_part<F>(&self, _callback: F)
- where
- F: FnMut(&AtomIdent),
- {
- }
-
- /// Internal iterator for the attribute names of this element.
- fn each_attr_name<F>(&self, callback: F)
- where
- F: FnMut(&LocalName);
-
- /// Internal iterator for the part names that this element exports for a
- /// given part name.
- fn each_exported_part<F>(&self, _name: &AtomIdent, _callback: F)
- where
- F: FnMut(&AtomIdent),
- {
- }
-
- /// Whether a given element may generate a pseudo-element.
- ///
- /// This is useful to avoid computing, for example, pseudo styles for
- /// `::-first-line` or `::-first-letter`, when we know it won't affect us.
- ///
- /// TODO(emilio, bz): actually implement the logic for it.
- fn may_generate_pseudo(&self, pseudo: &PseudoElement, _primary_style: &ComputedValues) -> bool {
- // ::before/::after are always supported for now, though we could try to
- // optimize out leaf elements.
-
- // ::first-letter and ::first-line are only supported for block-inside
- // things, and only in Gecko, not Servo. Unfortunately, Gecko has
- // block-inside things that might have any computed display value due to
- // things like fieldsets, legends, etc. Need to figure out how this
- // should work.
- debug_assert!(
- pseudo.is_eager(),
- "Someone called may_generate_pseudo with a non-eager pseudo."
- );
- true
- }
-
- /// Returns true if this element may have a descendant needing style processing.
- ///
- /// Note that we cannot guarantee the existence of such an element, because
- /// it may have been removed from the DOM between marking it for restyle and
- /// the actual restyle traversal.
- fn has_dirty_descendants(&self) -> bool;
-
- /// Returns whether state or attributes that may change style have changed
- /// on the element, and thus whether the element has been snapshotted to do
- /// restyle hint computation.
- fn has_snapshot(&self) -> bool;
-
- /// Returns whether the current snapshot if present has been handled.
- fn handled_snapshot(&self) -> bool;
-
- /// Flags this element as having handled already its snapshot.
- unsafe fn set_handled_snapshot(&self);
-
- /// Returns whether the element's styles are up-to-date after traversal
- /// (i.e. in post traversal).
- fn has_current_styles(&self, data: &ElementData) -> bool {
- if self.has_snapshot() && !self.handled_snapshot() {
- return false;
- }
-
- data.has_styles() &&
- // TODO(hiro): When an animating element moved into subtree of
- // contenteditable element, there remains animation restyle hints in
- // post traversal. It's generally harmless since the hints will be
- // processed in a next styling but ideally it should be processed soon.
- //
- // Without this, we get failures in:
- // layout/style/crashtests/1383319.html
- // layout/style/crashtests/1383001.html
- //
- // https://bugzilla.mozilla.org/show_bug.cgi?id=1389675 tracks fixing
- // this.
- !data.hint.has_non_animation_invalidations()
- }
-
- /// Flag that this element has a descendant for style processing.
- ///
- /// Only safe to call with exclusive access to the element.
- unsafe fn set_dirty_descendants(&self);
-
- /// Flag that this element has no descendant for style processing.
- ///
- /// Only safe to call with exclusive access to the element.
- unsafe fn unset_dirty_descendants(&self);
-
- /// Similar to the dirty_descendants but for representing a descendant of
- /// the element needs to be updated in animation-only traversal.
- fn has_animation_only_dirty_descendants(&self) -> bool {
- false
- }
-
- /// Flag that this element has a descendant for animation-only restyle
- /// processing.
- ///
- /// Only safe to call with exclusive access to the element.
- unsafe fn set_animation_only_dirty_descendants(&self) {}
-
- /// Flag that this element has no descendant for animation-only restyle processing.
- ///
- /// Only safe to call with exclusive access to the element.
- unsafe fn unset_animation_only_dirty_descendants(&self) {}
-
- /// Clear all bits related describing the dirtiness of descendants.
- ///
- /// In Gecko, this corresponds to the regular dirty descendants bit, the
- /// animation-only dirty descendants bit, and the lazy frame construction
- /// descendants bit.
- unsafe fn clear_descendant_bits(&self) {
- self.unset_dirty_descendants();
- }
-
- /// Returns true if this element is a visited link.
- ///
- /// Servo doesn't support visited styles yet.
- fn is_visited_link(&self) -> bool {
- false
- }
-
- /// Returns the pseudo-element implemented by this element, if any.
- ///
- /// Gecko traverses pseudo-elements during the style traversal, and we need
- /// to know this so we can properly grab the pseudo-element style from the
- /// parent element.
- ///
- /// Note that we still need to compute the pseudo-elements before-hand,
- /// given otherwise we don't know if we need to create an element or not.
- ///
- /// Servo doesn't have to deal with this.
- fn implemented_pseudo_element(&self) -> Option<PseudoElement> {
- None
- }
-
- /// Atomically stores the number of children of this node that we will
- /// need to process during bottom-up traversal.
- fn store_children_to_process(&self, n: isize);
-
- /// Atomically notes that a child has been processed during bottom-up
- /// traversal. Returns the number of children left to process.
- fn did_process_child(&self) -> isize;
-
- /// Gets a reference to the ElementData container, or creates one.
- ///
- /// Unsafe because it can race to allocate and leak if not used with
- /// exclusive access to the element.
- unsafe fn ensure_data(&self) -> AtomicRefMut<ElementData>;
-
- /// Clears the element data reference, if any.
- ///
- /// Unsafe following the same reasoning as ensure_data.
- unsafe fn clear_data(&self);
-
- /// Whether there is an ElementData container.
- fn has_data(&self) -> bool;
-
- /// Immutably borrows the ElementData.
- fn borrow_data(&self) -> Option<AtomicRef<ElementData>>;
-
- /// Mutably borrows the ElementData.
- fn mutate_data(&self) -> Option<AtomicRefMut<ElementData>>;
-
- /// Whether we should skip any root- or item-based display property
- /// blockification on this element. (This function exists so that Gecko
- /// native anonymous content can opt out of this style fixup.)
- fn skip_item_display_fixup(&self) -> bool;
-
- /// In Gecko, element has a flag that represents the element may have
- /// any type of animations or not to bail out animation stuff early.
- /// Whereas Servo doesn't have such flag.
- fn may_have_animations(&self) -> bool;
-
- /// Creates a task to update various animation state on a given (pseudo-)element.
- #[cfg(feature = "gecko")]
- fn update_animations(
- &self,
- before_change_style: Option<Arc<ComputedValues>>,
- tasks: UpdateAnimationsTasks,
- );
-
- /// Creates a task to process post animation on a given element.
- #[cfg(feature = "gecko")]
- fn process_post_animation(&self, tasks: PostAnimationTasks);
-
- /// Returns true if the element has relevant animations. Relevant
- /// animations are those animations that are affecting the element's style
- /// or are scheduled to do so in the future.
- fn has_animations(&self, context: &SharedStyleContext) -> bool;
-
- /// Returns true if the element has a CSS animation. The `context` and `pseudo_element`
- /// arguments are only used by Servo, since it stores animations globally and pseudo-elements
- /// are not in the DOM.
- fn has_css_animations(
- &self,
- context: &SharedStyleContext,
- pseudo_element: Option<PseudoElement>,
- ) -> bool;
-
- /// Returns true if the element has a CSS transition (including running transitions and
- /// completed transitions). The `context` and `pseudo_element` arguments are only used
- /// by Servo, since it stores animations globally and pseudo-elements are not in the DOM.
- fn has_css_transitions(
- &self,
- context: &SharedStyleContext,
- pseudo_element: Option<PseudoElement>,
- ) -> bool;
-
- /// Returns true if the element has animation restyle hints.
- fn has_animation_restyle_hints(&self) -> bool {
- let data = match self.borrow_data() {
- Some(d) => d,
- None => return false,
- };
- return data.hint.has_animation_hint();
- }
-
- /// The shadow root this element is a host of.
- fn shadow_root(&self) -> Option<<Self::ConcreteNode as TNode>::ConcreteShadowRoot>;
-
- /// The shadow root which roots the subtree this element is contained in.
- fn containing_shadow(&self) -> Option<<Self::ConcreteNode as TNode>::ConcreteShadowRoot>;
-
- /// Return the element which we can use to look up rules in the selector
- /// maps.
- ///
- /// This is always the element itself, except in the case where we are an
- /// element-backed pseudo-element, in which case we return the originating
- /// element.
- fn rule_hash_target(&self) -> Self {
- if self.is_pseudo_element() {
- self.pseudo_element_originating_element()
- .expect("Trying to collect rules for a detached pseudo-element")
- } else {
- *self
- }
- }
-
- /// Executes the callback for each applicable style rule data which isn't
- /// the main document's data (which stores UA / author rules).
- ///
- /// The element passed to the callback is the containing shadow host for the
- /// data if it comes from Shadow DOM.
- ///
- /// Returns whether normal document author rules should apply.
- ///
- /// TODO(emilio): We could separate the invalidation data for elements
- /// matching in other scopes to avoid over-invalidation.
- fn each_applicable_non_document_style_rule_data<'a, F>(&self, mut f: F) -> bool
- where
- Self: 'a,
- F: FnMut(&'a CascadeData, Self),
- {
- use crate::rule_collector::containing_shadow_ignoring_svg_use;
-
- let target = self.rule_hash_target();
- let matches_user_and_content_rules = target.matches_user_and_content_rules();
- let mut doc_rules_apply = matches_user_and_content_rules;
-
- // Use the same rules to look for the containing host as we do for rule
- // collection.
- if let Some(shadow) = containing_shadow_ignoring_svg_use(target) {
- doc_rules_apply = false;
- if let Some(data) = shadow.style_data() {
- f(data, shadow.host());
- }
- }
-
- if let Some(shadow) = target.shadow_root() {
- if let Some(data) = shadow.style_data() {
- f(data, shadow.host());
- }
- }
-
- let mut current = target.assigned_slot();
- while let Some(slot) = current {
- // Slots can only have assigned nodes when in a shadow tree.
- let shadow = slot.containing_shadow().unwrap();
- if let Some(data) = shadow.style_data() {
- if data.any_slotted_rule() {
- f(data, shadow.host());
- }
- }
- current = slot.assigned_slot();
- }
-
- if target.has_part_attr() {
- if let Some(mut inner_shadow) = target.containing_shadow() {
- loop {
- let inner_shadow_host = inner_shadow.host();
- match inner_shadow_host.containing_shadow() {
- Some(shadow) => {
- if let Some(data) = shadow.style_data() {
- if data.any_part_rule() {
- f(data, shadow.host())
- }
- }
- // TODO: Could be more granular.
- if !inner_shadow_host.exports_any_part() {
- break;
- }
- inner_shadow = shadow;
- },
- None => {
- // TODO(emilio): Should probably distinguish with
- // MatchesDocumentRules::{No,Yes,IfPart} or something so that we could
- // skip some work.
- doc_rules_apply = matches_user_and_content_rules;
- break;
- },
- }
- }
- }
- }
-
- doc_rules_apply
- }
-
- /// Returns true if one of the transitions needs to be updated on this element. We check all
- /// the transition properties to make sure that updating transitions is necessary.
- /// This method should only be called if might_needs_transitions_update returns true when
- /// passed the same parameters.
- #[cfg(feature = "gecko")]
- fn needs_transitions_update(
- &self,
- before_change_style: &ComputedValues,
- after_change_style: &ComputedValues,
- ) -> bool;
-
- /// Returns the value of the `xml:lang=""` attribute (or, if appropriate,
- /// the `lang=""` attribute) on this element.
- fn lang_attr(&self) -> Option<AttrValue>;
-
- /// Returns whether this element's language matches the language tag
- /// `value`. If `override_lang` is not `None`, it specifies the value
- /// of the `xml:lang=""` or `lang=""` attribute to use in place of
- /// looking at the element and its ancestors. (This argument is used
- /// to implement matching of `:lang()` against snapshots.)
- fn match_element_lang(&self, override_lang: Option<Option<AttrValue>>, value: &Lang) -> bool;
-
- /// Returns whether this element is the main body element of the HTML
- /// document it is on.
- fn is_html_document_body_element(&self) -> bool;
-
- /// Generate the proper applicable declarations due to presentational hints,
- /// and insert them into `hints`.
- fn synthesize_presentational_hints_for_legacy_attributes<V>(
- &self,
- visited_handling: VisitedHandlingMode,
- hints: &mut V,
- ) where
- V: Push<ApplicableDeclarationBlock>;
-
- /// Returns element's local name.
- fn local_name(&self) -> &<SelectorImpl as selectors::parser::SelectorImpl>::BorrowedLocalName;
-
- /// Returns element's namespace.
- fn namespace(&self)
- -> &<SelectorImpl as selectors::parser::SelectorImpl>::BorrowedNamespaceUrl;
-
- /// Returns the size of the element to be used in container size queries.
- /// This will usually be the size of the content area of the primary box,
- /// but can be None if there is no box or if some axis lacks size containment.
- fn query_container_size(
- &self,
- display: &Display,
- ) -> euclid::default::Size2D<Option<app_units::Au>>;
-}
-
-/// TNode and TElement aren't Send because we want to be careful and explicit
-/// about our parallel traversal. However, there are certain situations
-/// (including but not limited to the traversal) where we need to send DOM
-/// objects to other threads.
-///
-/// That's the reason why `SendNode` exists.
-#[derive(Clone, Debug, PartialEq)]
-pub struct SendNode<N: TNode>(N);
-unsafe impl<N: TNode> Send for SendNode<N> {}
-impl<N: TNode> SendNode<N> {
- /// Unsafely construct a SendNode.
- pub unsafe fn new(node: N) -> Self {
- SendNode(node)
- }
-}
-impl<N: TNode> Deref for SendNode<N> {
- type Target = N;
- fn deref(&self) -> &N {
- &self.0
- }
-}
-
-/// Same reason as for the existence of SendNode, SendElement does the proper
-/// things for a given `TElement`.
-#[derive(Debug, Eq, Hash, PartialEq)]
-pub struct SendElement<E: TElement>(E);
-unsafe impl<E: TElement> Send for SendElement<E> {}
-impl<E: TElement> SendElement<E> {
- /// Unsafely construct a SendElement.
- pub unsafe fn new(el: E) -> Self {
- SendElement(el)
- }
-}
-impl<E: TElement> Deref for SendElement<E> {
- type Target = E;
- fn deref(&self) -> &E {
- &self.0
- }
-}
diff --git a/components/style/dom_apis.rs b/components/style/dom_apis.rs
deleted file mode 100644
index da5dc387f73..00000000000
--- a/components/style/dom_apis.rs
+++ /dev/null
@@ -1,767 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
-
-//! Generic implementations of some DOM APIs so they can be shared between Servo
-//! and Gecko.
-
-use crate::context::QuirksMode;
-use crate::dom::{TDocument, TElement, TNode, TShadowRoot};
-use crate::invalidation::element::invalidation_map::Dependency;
-use crate::invalidation::element::invalidator::{DescendantInvalidationLists, Invalidation};
-use crate::invalidation::element::invalidator::{InvalidationProcessor, InvalidationVector};
-use crate::selector_parser::SelectorImpl;
-use crate::values::AtomIdent;
-use selectors::attr::CaseSensitivity;
-use selectors::matching::{self, MatchingContext, MatchingMode, NeedsSelectorFlags};
-use selectors::parser::{Combinator, Component, LocalName};
-use selectors::{Element, SelectorList};
-use smallvec::SmallVec;
-
-/// <https://dom.spec.whatwg.org/#dom-element-matches>
-pub fn element_matches<E>(
- element: &E,
- selector_list: &SelectorList<E::Impl>,
- quirks_mode: QuirksMode,
-) -> bool
-where
- E: Element,
-{
- let mut nth_index_cache = Default::default();
-
- let mut context = MatchingContext::new(
- MatchingMode::Normal,
- None,
- &mut nth_index_cache,
- quirks_mode,
- NeedsSelectorFlags::No,
- );
- context.scope_element = Some(element.opaque());
- context.current_host = element.containing_shadow_host().map(|e| e.opaque());
- matching::matches_selector_list(selector_list, element, &mut context)
-}
-
-/// <https://dom.spec.whatwg.org/#dom-element-closest>
-pub fn element_closest<E>(
- element: E,
- selector_list: &SelectorList<E::Impl>,
- quirks_mode: QuirksMode,
-) -> Option<E>
-where
- E: Element,
-{
- let mut nth_index_cache = Default::default();
-
- let mut context = MatchingContext::new(
- MatchingMode::Normal,
- None,
- &mut nth_index_cache,
- quirks_mode,
- NeedsSelectorFlags::No,
- );
- context.scope_element = Some(element.opaque());
- context.current_host = element.containing_shadow_host().map(|e| e.opaque());
-
- let mut current = Some(element);
- while let Some(element) = current.take() {
- if matching::matches_selector_list(selector_list, &element, &mut context) {
- return Some(element);
- }
- current = element.parent_element();
- }
-
- return None;
-}
-
-/// A selector query abstraction, in order to be generic over QuerySelector and
-/// QuerySelectorAll.
-pub trait SelectorQuery<E: TElement> {
- /// The output of the query.
- type Output;
-
- /// Whether the query should stop after the first element has been matched.
- fn should_stop_after_first_match() -> bool;
-
- /// Append an element matching after the first query.
- fn append_element(output: &mut Self::Output, element: E);
-
- /// Returns true if the output is empty.
- fn is_empty(output: &Self::Output) -> bool;
-}
-
-/// The result of a querySelectorAll call.
-pub type QuerySelectorAllResult<E> = SmallVec<[E; 128]>;
-
-/// A query for all the elements in a subtree.
-pub struct QueryAll;
-
-impl<E: TElement> SelectorQuery<E> for QueryAll {
- type Output = QuerySelectorAllResult<E>;
-
- fn should_stop_after_first_match() -> bool {
- false
- }
-
- fn append_element(output: &mut Self::Output, element: E) {
- output.push(element);
- }
-
- fn is_empty(output: &Self::Output) -> bool {
- output.is_empty()
- }
-}
-
-/// A query for the first in-tree match of all the elements in a subtree.
-pub struct QueryFirst;
-
-impl<E: TElement> SelectorQuery<E> for QueryFirst {
- type Output = Option<E>;
-
- fn should_stop_after_first_match() -> bool {
- true
- }
-
- fn append_element(output: &mut Self::Output, element: E) {
- if output.is_none() {
- *output = Some(element)
- }
- }
-
- fn is_empty(output: &Self::Output) -> bool {
- output.is_none()
- }
-}
-
-struct QuerySelectorProcessor<'a, E, Q>
-where
- E: TElement + 'a,
- Q: SelectorQuery<E>,
- Q::Output: 'a,
-{
- results: &'a mut Q::Output,
- matching_context: MatchingContext<'a, E::Impl>,
- dependencies: &'a [Dependency],
-}
-
-impl<'a, E, Q> InvalidationProcessor<'a, E> for QuerySelectorProcessor<'a, E, Q>
-where
- E: TElement + 'a,
- Q: SelectorQuery<E>,
- Q::Output: 'a,
-{
- fn light_tree_only(&self) -> bool {
- true
- }
-
- fn check_outer_dependency(&mut self, _: &Dependency, _: E) -> bool {
- debug_assert!(
- false,
- "How? We should only have parent-less dependencies here!"
- );
- true
- }
-
- fn collect_invalidations(
- &mut self,
- element: E,
- self_invalidations: &mut InvalidationVector<'a>,
- descendant_invalidations: &mut DescendantInvalidationLists<'a>,
- _sibling_invalidations: &mut InvalidationVector<'a>,
- ) -> bool {
- // TODO(emilio): If the element is not a root element, and
- // selector_list has any descendant combinator, we need to do extra work
- // in order to handle properly things like:
- //
- // <div id="a">
- // <div id="b">
- // <div id="c"></div>
- // </div>
- // </div>
- //
- // b.querySelector('#a div'); // Should return "c".
- //
- // For now, assert it's a root element.
- debug_assert!(element.parent_element().is_none());
-
- let target_vector = if self.matching_context.scope_element.is_some() {
- &mut descendant_invalidations.dom_descendants
- } else {
- self_invalidations
- };
-
- for dependency in self.dependencies.iter() {
- target_vector.push(Invalidation::new(
- dependency,
- self.matching_context.current_host.clone(),
- ))
- }
-
- false
- }
-
- fn matching_context(&mut self) -> &mut MatchingContext<'a, E::Impl> {
- &mut self.matching_context
- }
-
- fn should_process_descendants(&mut self, _: E) -> bool {
- if Q::should_stop_after_first_match() {
- return Q::is_empty(&self.results);
- }
-
- true
- }
-
- fn invalidated_self(&mut self, e: E) {
- Q::append_element(self.results, e);
- }
-
- fn invalidated_sibling(&mut self, e: E, _of: E) {
- Q::append_element(self.results, e);
- }
-
- fn recursion_limit_exceeded(&mut self, _e: E) {}
- fn invalidated_descendants(&mut self, _e: E, _child: E) {}
-}
-
-fn collect_all_elements<E, Q, F>(root: E::ConcreteNode, results: &mut Q::Output, mut filter: F)
-where
- E: TElement,
- Q: SelectorQuery<E>,
- F: FnMut(E) -> bool,
-{
- for node in root.dom_descendants() {
- let element = match node.as_element() {
- Some(e) => e,
- None => continue,
- };
-
- if !filter(element) {
- continue;
- }
-
- Q::append_element(results, element);
- if Q::should_stop_after_first_match() {
- return;
- }
- }
-}
-
-/// Returns whether a given element connected to `root` is descendant of `root`.
-///
-/// NOTE(emilio): if root == element, this returns false.
-fn connected_element_is_descendant_of<E>(element: E, root: E::ConcreteNode) -> bool
-where
- E: TElement,
-{
- // Optimize for when the root is a document or a shadow root and the element
- // is connected to that root.
- if root.as_document().is_some() {
- debug_assert!(element.as_node().is_in_document(), "Not connected?");
- debug_assert_eq!(
- root,
- root.owner_doc().as_node(),
- "Where did this element come from?",
- );
- return true;
- }
-
- if root.as_shadow_root().is_some() {
- debug_assert_eq!(
- element.containing_shadow().unwrap().as_node(),
- root,
- "Not connected?"
- );
- return true;
- }
-
- let mut current = element.as_node().parent_node();
- while let Some(n) = current.take() {
- if n == root {
- return true;
- }
-
- current = n.parent_node();
- }
- false
-}
-
-/// Fast path for iterating over every element with a given id in the document
-/// or shadow root that `root` is connected to.
-fn fast_connected_elements_with_id<'a, N>(
- root: N,
- id: &AtomIdent,
- case_sensitivity: CaseSensitivity,
-) -> Result<&'a [N::ConcreteElement], ()>
-where
- N: TNode + 'a,
-{
- if case_sensitivity != CaseSensitivity::CaseSensitive {
- return Err(());
- }
-
- if root.is_in_document() {
- return root.owner_doc().elements_with_id(id);
- }
-
- if let Some(shadow) = root.as_shadow_root() {
- return shadow.elements_with_id(id);
- }
-
- if let Some(shadow) = root.as_element().and_then(|e| e.containing_shadow()) {
- return shadow.elements_with_id(id);
- }
-
- Err(())
-}
-
-/// Collects elements with a given id under `root`, that pass `filter`.
-fn collect_elements_with_id<E, Q, F>(
- root: E::ConcreteNode,
- id: &AtomIdent,
- results: &mut Q::Output,
- class_and_id_case_sensitivity: CaseSensitivity,
- mut filter: F,
-) where
- E: TElement,
- Q: SelectorQuery<E>,
- F: FnMut(E) -> bool,
-{
- let elements = match fast_connected_elements_with_id(root, id, class_and_id_case_sensitivity) {
- Ok(elements) => elements,
- Err(()) => {
- collect_all_elements::<E, Q, _>(root, results, |e| {
- e.has_id(id, class_and_id_case_sensitivity) && filter(e)
- });
-
- return;
- },
- };
-
- for element in elements {
- // If the element is not an actual descendant of the root, even though
- // it's connected, we don't really care about it.
- if !connected_element_is_descendant_of(*element, root) {
- continue;
- }
-
- if !filter(*element) {
- continue;
- }
-
- Q::append_element(results, *element);
- if Q::should_stop_after_first_match() {
- break;
- }
- }
-}
-
-fn has_attr<E>(element: E, local_name: &crate::LocalName) -> bool
-where
- E: TElement,
-{
- let mut found = false;
- element.each_attr_name(|name| found |= name == local_name);
- found
-}
-
-#[inline(always)]
-fn local_name_matches<E>(element: E, local_name: &LocalName<E::Impl>) -> bool
-where
- E: TElement,
-{
- let LocalName {
- ref name,
- ref lower_name,
- } = *local_name;
-
- let chosen_name = if name == lower_name || element.is_html_element_in_html_document() {
- lower_name
- } else {
- name
- };
-
- element.local_name() == &**chosen_name
-}
-
-fn get_attr_name(component: &Component<SelectorImpl>) -> Option<&crate::LocalName> {
- let (name, name_lower) = match component {
- Component::AttributeInNoNamespace { ref local_name, .. } => return Some(local_name),
- Component::AttributeInNoNamespaceExists {
- ref local_name,
- ref local_name_lower,
- ..
- } => (local_name, local_name_lower),
- Component::AttributeOther(ref attr) => (&attr.local_name, &attr.local_name_lower),
- _ => return None,
- };
- if name != name_lower {
- return None; // TODO: Maybe optimize this?
- }
- Some(name)
-}
-
-fn get_id(component: &Component<SelectorImpl>) -> Option<&AtomIdent> {
- use selectors::attr::AttrSelectorOperator;
- Some(match component {
- Component::ID(ref id) => id,
- Component::AttributeInNoNamespace {
- ref operator,
- ref local_name,
- ref value,
- ..
- } => {
- if *local_name != local_name!("id") {
- return None;
- }
- if *operator != AttrSelectorOperator::Equal {
- return None;
- }
- AtomIdent::cast(&value.0)
- },
- _ => return None,
- })
-}
-
-/// Fast paths for querySelector with a single simple selector.
-fn query_selector_single_query<E, Q>(
- root: E::ConcreteNode,
- component: &Component<E::Impl>,
- results: &mut Q::Output,
- class_and_id_case_sensitivity: CaseSensitivity,
-) -> Result<(), ()>
-where
- E: TElement,
- Q: SelectorQuery<E>,
-{
- // TODO: Maybe we could implement a fast path for [name=""]?
- match *component {
- Component::ExplicitUniversalType => {
- collect_all_elements::<E, Q, _>(root, results, |_| true)
- },
- Component::Class(ref class) => collect_all_elements::<E, Q, _>(root, results, |element| {
- element.has_class(class, class_and_id_case_sensitivity)
- }),
- Component::LocalName(ref local_name) => {
- collect_all_elements::<E, Q, _>(root, results, |element| {
- local_name_matches(element, local_name)
- })
- },
- ref other => {
- let id = match get_id(other) {
- Some(id) => id,
- // TODO(emilio): More fast paths?
- None => return Err(()),
- };
- collect_elements_with_id::<E, Q, _>(
- root,
- id,
- results,
- class_and_id_case_sensitivity,
- |_| true,
- );
- },
- }
-
- Ok(())
-}
-
-enum SimpleFilter<'a> {
- Class(&'a AtomIdent),
- Attr(&'a crate::LocalName),
- LocalName(&'a LocalName<SelectorImpl>),
-}
-
-/// Fast paths for a given selector query.
-///
-/// When there's only one component, we go directly to
-/// `query_selector_single_query`, otherwise, we try to optimize by looking just
-/// at the subtrees rooted at ids in the selector, and otherwise we try to look
-/// up by class name or local name in the rightmost compound.
-///
-/// FIXME(emilio, nbp): This may very well be a good candidate for code to be
-/// replaced by HolyJit :)
-fn query_selector_fast<E, Q>(
- root: E::ConcreteNode,
- selector_list: &SelectorList<E::Impl>,
- results: &mut Q::Output,
- matching_context: &mut MatchingContext<E::Impl>,
-) -> Result<(), ()>
-where
- E: TElement,
- Q: SelectorQuery<E>,
-{
- // We need to return elements in document order, and reordering them
- // afterwards is kinda silly.
- if selector_list.0.len() > 1 {
- return Err(());
- }
-
- let selector = &selector_list.0[0];
- let class_and_id_case_sensitivity = matching_context.classes_and_ids_case_sensitivity();
- // Let's just care about the easy cases for now.
- if selector.len() == 1 {
- if query_selector_single_query::<E, Q>(
- root,
- selector.iter().next().unwrap(),
- results,
- class_and_id_case_sensitivity,
- )
- .is_ok()
- {
- return Ok(());
- }
- }
-
- let mut iter = selector.iter();
- let mut combinator: Option<Combinator> = None;
-
- // We want to optimize some cases where there's no id involved whatsoever,
- // like `.foo .bar`, but we don't want to make `#foo .bar` slower because of
- // that.
- let mut simple_filter = None;
-
- 'selector_loop: loop {
- debug_assert!(combinator.map_or(true, |c| !c.is_sibling()));
-
- 'component_loop: for component in &mut iter {
- match *component {
- Component::Class(ref class) => {
- if combinator.is_none() {
- simple_filter = Some(SimpleFilter::Class(class));
- }
- },
- Component::LocalName(ref local_name) => {
- if combinator.is_none() {
- // Prefer to look at class rather than local-name if
- // both are present.
- if let Some(SimpleFilter::Class(..)) = simple_filter {
- continue;
- }
- simple_filter = Some(SimpleFilter::LocalName(local_name));
- }
- },
- ref other => {
- if let Some(id) = get_id(other) {
- if combinator.is_none() {
- // In the rightmost compound, just find descendants of root that match
- // the selector list with that id.
- collect_elements_with_id::<E, Q, _>(
- root,
- id,
- results,
- class_and_id_case_sensitivity,
- |e| {
- matching::matches_selector_list(
- selector_list,
- &e,
- matching_context,
- )
- },
- );
- return Ok(());
- }
-
- let elements = fast_connected_elements_with_id(
- root,
- id,
- class_and_id_case_sensitivity,
- )?;
- if elements.is_empty() {
- return Ok(());
- }
-
- // Results need to be in document order. Let's not bother
- // reordering or deduplicating nodes, which we would need to
- // do if one element with the given id were a descendant of
- // another element with that given id.
- if !Q::should_stop_after_first_match() && elements.len() > 1 {
- continue;
- }
-
- for element in elements {
- // If the element is not a descendant of the root, then
- // it may have descendants that match our selector that
- // _are_ descendants of the root, and other descendants
- // that match our selector that are _not_.
- //
- // So we can't just walk over the element's descendants
- // and match the selector against all of them, nor can
- // we skip looking at this element's descendants.
- //
- // Give up on trying to optimize based on this id and
- // keep walking our selector.
- if !connected_element_is_descendant_of(*element, root) {
- continue 'component_loop;
- }
-
- query_selector_slow::<E, Q>(
- element.as_node(),
- selector_list,
- results,
- matching_context,
- );
-
- if Q::should_stop_after_first_match() && !Q::is_empty(&results) {
- break;
- }
- }
-
- return Ok(());
- }
- if combinator.is_none() && simple_filter.is_none() {
- if let Some(attr_name) = get_attr_name(other) {
- simple_filter = Some(SimpleFilter::Attr(attr_name));
- }
- }
- },
- }
- }
-
- loop {
- let next_combinator = match iter.next_sequence() {
- None => break 'selector_loop,
- Some(c) => c,
- };
-
- // We don't want to scan stuff affected by sibling combinators,
- // given we scan the subtree of elements with a given id (and we
- // don't want to care about scanning the siblings' subtrees).
- if next_combinator.is_sibling() {
- // Advance to the next combinator.
- for _ in &mut iter {}
- continue;
- }
-
- combinator = Some(next_combinator);
- break;
- }
- }
-
- // We got here without finding any ID or such that we could handle. Try to
- // use one of the simple filters.
- let simple_filter = match simple_filter {
- Some(f) => f,
- None => return Err(()),
- };
-
- match simple_filter {
- SimpleFilter::Class(ref class) => {
- collect_all_elements::<E, Q, _>(root, results, |element| {
- element.has_class(class, class_and_id_case_sensitivity) &&
- matching::matches_selector_list(selector_list, &element, matching_context)
- });
- },
- SimpleFilter::LocalName(ref local_name) => {
- collect_all_elements::<E, Q, _>(root, results, |element| {
- local_name_matches(element, local_name) &&
- matching::matches_selector_list(selector_list, &element, matching_context)
- });
- },
- SimpleFilter::Attr(ref local_name) => {
- collect_all_elements::<E, Q, _>(root, results, |element| {
- has_attr(element, local_name) &&
- matching::matches_selector_list(selector_list, &element, matching_context)
- });
- },
- }
-
- Ok(())
-}
-
-// Slow path for a given selector query.
-fn query_selector_slow<E, Q>(
- root: E::ConcreteNode,
- selector_list: &SelectorList<E::Impl>,
- results: &mut Q::Output,
- matching_context: &mut MatchingContext<E::Impl>,
-) where
- E: TElement,
- Q: SelectorQuery<E>,
-{
- collect_all_elements::<E, Q, _>(root, results, |element| {
- matching::matches_selector_list(selector_list, &element, matching_context)
- });
-}
-
-/// Whether the invalidation machinery should be used for this query.
-#[derive(PartialEq)]
-pub enum MayUseInvalidation {
- /// We may use it if we deem it useful.
- Yes,
- /// Don't use it.
- No,
-}
-
-/// <https://dom.spec.whatwg.org/#dom-parentnode-queryselector>
-pub fn query_selector<E, Q>(
- root: E::ConcreteNode,
- selector_list: &SelectorList<E::Impl>,
- results: &mut Q::Output,
- may_use_invalidation: MayUseInvalidation,
-) where
- E: TElement,
- Q: SelectorQuery<E>,
-{
- use crate::invalidation::element::invalidator::TreeStyleInvalidator;
-
- let mut nth_index_cache = Default::default();
- let quirks_mode = root.owner_doc().quirks_mode();
-
- let mut matching_context = MatchingContext::new(
- MatchingMode::Normal,
- None,
- &mut nth_index_cache,
- quirks_mode,
- NeedsSelectorFlags::No,
- );
- let root_element = root.as_element();
- matching_context.scope_element = root_element.map(|e| e.opaque());
- matching_context.current_host = match root_element {
- Some(root) => root.containing_shadow_host().map(|host| host.opaque()),
- None => root.as_shadow_root().map(|root| root.host().opaque()),
- };
-
- let fast_result =
- query_selector_fast::<E, Q>(root, selector_list, results, &mut matching_context);
-
- if fast_result.is_ok() {
- return;
- }
-
- // Slow path: Use the invalidation machinery if we're a root, and tree
- // traversal otherwise.
- //
- // See the comment in collect_invalidations to see why only if we're a root.
- //
- // The invalidation mechanism is only useful in presence of combinators.
- //
- // We could do that check properly here, though checking the length of the
- // selectors is a good heuristic.
- //
- // A selector with a combinator needs to have a length of at least 3: A
- // simple selector, a combinator, and another simple selector.
- let invalidation_may_be_useful = may_use_invalidation == MayUseInvalidation::Yes &&
- selector_list.0.iter().any(|s| s.len() > 2);
-
- if root_element.is_some() || !invalidation_may_be_useful {
- query_selector_slow::<E, Q>(root, selector_list, results, &mut matching_context);
- } else {
- let dependencies = selector_list
- .0
- .iter()
- .map(|selector| Dependency::for_full_selector_invalidation(selector.clone()))
- .collect::<SmallVec<[_; 5]>>();
- let mut processor = QuerySelectorProcessor::<E, Q> {
- results,
- matching_context,
- dependencies: &dependencies,
- };
-
- for node in root.dom_children() {
- if let Some(e) = node.as_element() {
- TreeStyleInvalidator::new(e, /* stack_limit_checker = */ None, &mut processor)
- .invalidate();
- }
- }
- }
-}
diff --git a/components/style/driver.rs b/components/style/driver.rs
deleted file mode 100644
index cf48d831fdd..00000000000
--- a/components/style/driver.rs
+++ /dev/null
@@ -1,161 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
-
-//! Implements traversal over the DOM tree. The traversal starts in sequential
-//! mode, and optionally parallelizes as it discovers work.
-
-#![deny(missing_docs)]
-
-use crate::context::{PerThreadTraversalStatistics, StyleContext};
-use crate::context::{ThreadLocalStyleContext, TraversalStatistics};
-use crate::dom::{SendNode, TElement, TNode};
-use crate::parallel;
-use crate::scoped_tls::ScopedTLS;
-use crate::traversal::{DomTraversal, PerLevelTraversalData, PreTraverseToken};
-use rayon;
-use std::collections::VecDeque;
-use std::mem;
-use time;
-
-#[cfg(feature = "servo")]
-fn should_report_statistics() -> bool {
- false
-}
-
-#[cfg(feature = "gecko")]
-fn should_report_statistics() -> bool {
- unsafe { crate::gecko_bindings::structs::ServoTraversalStatistics_sActive }
-}
-
-#[cfg(feature = "servo")]
-fn report_statistics(_stats: &PerThreadTraversalStatistics) {
- unreachable!("Servo never report stats");
-}
-
-#[cfg(feature = "gecko")]
-fn report_statistics(stats: &PerThreadTraversalStatistics) {
- // This should only be called in the main thread, or it may be racy
- // to update the statistics in a global variable.
- debug_assert!(unsafe { crate::gecko_bindings::bindings::Gecko_IsMainThread() });
- let gecko_stats =
- unsafe { &mut crate::gecko_bindings::structs::ServoTraversalStatistics_sSingleton };
- gecko_stats.mElementsTraversed += stats.elements_traversed;
- gecko_stats.mElementsStyled += stats.elements_styled;
- gecko_stats.mElementsMatched += stats.elements_matched;
- gecko_stats.mStylesShared += stats.styles_shared;
- gecko_stats.mStylesReused += stats.styles_reused;
-}
-
-fn with_pool_in_place_scope<'scope, R>(
- work_unit_max: usize,
- pool: Option<&rayon::ThreadPool>,
- closure: impl FnOnce(Option<&rayon::ScopeFifo<'scope>>) -> R,
-) -> R {
- if work_unit_max == 0 || pool.is_none() {
- closure(None)
- } else {
- pool.unwrap().in_place_scope_fifo(|scope| {
- closure(Some(scope))
- })
- }
-}
-
-/// See documentation of the pref for performance characteristics.
-fn work_unit_max() -> usize {
- static_prefs::pref!("layout.css.stylo-work-unit-size") as usize
-}
-
-/// Do a DOM traversal for top-down and (optionally) bottom-up processing, generic over `D`.
-///
-/// We use an adaptive traversal strategy. We start out with simple sequential processing, until we
-/// arrive at a wide enough level in the DOM that the parallel traversal would parallelize it.
-/// If a thread pool is provided, we then transfer control over to the parallel traversal.
-///
-/// Returns true if the traversal was parallel, and also returns the statistics object containing
-/// information on nodes traversed (on nightly only). Not all of its fields will be initialized
-/// since we don't call finish().
-pub fn traverse_dom<E, D>(
- traversal: &D,
- token: PreTraverseToken<E>,
- pool: Option<&rayon::ThreadPool>,
-) -> E
-where
- E: TElement,
- D: DomTraversal<E>,
-{
- let root = token
- .traversal_root()
- .expect("Should've ensured we needed to traverse");
-
- let report_stats = should_report_statistics();
- let dump_stats = traversal.shared_context().options.dump_style_statistics;
- let start_time = if dump_stats {
- Some(time::precise_time_s())
- } else {
- None
- };
-
- // Declare the main-thread context, as well as the worker-thread contexts,
- // which we may or may not instantiate. It's important to declare the worker-
- // thread contexts first, so that they get dropped second. This matters because:
- // * ThreadLocalContexts borrow AtomicRefCells in TLS.
- // * Dropping a ThreadLocalContext can run SequentialTasks.
- // * Sequential tasks may call into functions like
- // Servo_StyleSet_GetBaseComputedValuesForElement, which instantiate a
- // ThreadLocalStyleContext on the main thread. If the main thread
- // ThreadLocalStyleContext has not released its TLS borrow by that point,
- // we'll panic on double-borrow.
- let mut scoped_tls = pool.map(ScopedTLS::<ThreadLocalStyleContext<E>>::new);
- let mut tlc = ThreadLocalStyleContext::new();
- let mut context = StyleContext {
- shared: traversal.shared_context(),
- thread_local: &mut tlc,
- };
-
- // Process the nodes breadth-first. This helps keep similar traversal characteristics for the
- // style sharing cache.
- let work_unit_max = work_unit_max();
- with_pool_in_place_scope(work_unit_max, pool, |maybe_scope| {
- let mut discovered = VecDeque::with_capacity(work_unit_max * 2);
- discovered.push_back(unsafe { SendNode::new(root.as_node()) });
- parallel::style_trees(
- &mut context,
- discovered,
- root.as_node().opaque(),
- work_unit_max,
- static_prefs::pref!("layout.css.stylo-local-work-queue.in-main-thread") as usize,
- PerLevelTraversalData { current_dom_depth: root.depth() },
- maybe_scope,
- traversal,
- scoped_tls.as_ref(),
- );
- });
-
- // Collect statistics from thread-locals if requested.
- if dump_stats || report_stats {
- let mut aggregate = mem::replace(&mut context.thread_local.statistics, Default::default());
- let parallel = pool.is_some();
- if let Some(ref mut tls) = scoped_tls {
- for slot in tls.slots() {
- if let Some(cx) = slot.get_mut() {
- aggregate += cx.statistics.clone();
- }
- }
- }
-
- if report_stats {
- report_statistics(&aggregate);
- }
- // dump statistics to stdout if requested
- if dump_stats {
- let stats =
- TraversalStatistics::new(aggregate, traversal, parallel, start_time.unwrap());
- if stats.is_large {
- println!("{}", stats);
- }
- }
- }
-
- root
-}
diff --git a/components/style/encoding_support.rs b/components/style/encoding_support.rs
deleted file mode 100644
index c144ad0b3bc..00000000000
--- a/components/style/encoding_support.rs
+++ /dev/null
@@ -1,105 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
-
-//! Parsing stylesheets from bytes (not `&str`).
-
-use crate::context::QuirksMode;
-use crate::error_reporting::ParseErrorReporter;
-use crate::media_queries::MediaList;
-use crate::shared_lock::SharedRwLock;
-use crate::stylesheets::{AllowImportRules, Origin, Stylesheet, StylesheetLoader, UrlExtraData};
-use cssparser::{stylesheet_encoding, EncodingSupport};
-use servo_arc::Arc;
-use std::borrow::Cow;
-use std::str;
-
-struct EncodingRs;
-
-impl EncodingSupport for EncodingRs {
- type Encoding = &'static encoding_rs::Encoding;
-
- fn utf8() -> Self::Encoding {
- encoding_rs::UTF_8
- }
-
- fn is_utf16_be_or_le(encoding: &Self::Encoding) -> bool {
- *encoding == encoding_rs::UTF_16LE || *encoding == encoding_rs::UTF_16BE
- }
-
- fn from_label(ascii_label: &[u8]) -> Option<Self::Encoding> {
- encoding_rs::Encoding::for_label(ascii_label)
- }
-}
-
-fn decode_stylesheet_bytes<'a>(
- css: &'a [u8],
- protocol_encoding_label: Option<&str>,
- environment_encoding: Option<&'static encoding_rs::Encoding>,
-) -> Cow<'a, str> {
- let fallback_encoding = stylesheet_encoding::<EncodingRs>(
- css,
- protocol_encoding_label.map(str::as_bytes),
- environment_encoding,
- );
- let (result, _used_encoding, _) = fallback_encoding.decode(&css);
- // FIXME record used encoding for environment encoding of @import
- result
-}
-
-impl Stylesheet {
- /// Parse a stylesheet from a set of bytes, potentially received over the
- /// network.
- ///
- /// Takes care of decoding the network bytes and forwards the resulting
- /// string to `Stylesheet::from_str`.
- pub fn from_bytes(
- bytes: &[u8],
- url_data: UrlExtraData,
- protocol_encoding_label: Option<&str>,
- environment_encoding: Option<&'static encoding_rs::Encoding>,
- origin: Origin,
- media: MediaList,
- shared_lock: SharedRwLock,
- stylesheet_loader: Option<&dyn StylesheetLoader>,
- error_reporter: Option<&dyn ParseErrorReporter>,
- quirks_mode: QuirksMode,
- ) -> Stylesheet {
- let string = decode_stylesheet_bytes(bytes, protocol_encoding_label, environment_encoding);
- Stylesheet::from_str(
- &string,
- url_data,
- origin,
- Arc::new(shared_lock.wrap(media)),
- shared_lock,
- stylesheet_loader,
- error_reporter,
- quirks_mode,
- 0,
- AllowImportRules::Yes,
- )
- }
-
- /// Updates an empty stylesheet with a set of bytes that reached over the
- /// network.
- pub fn update_from_bytes(
- existing: &Stylesheet,
- bytes: &[u8],
- protocol_encoding_label: Option<&str>,
- environment_encoding: Option<&'static encoding_rs::Encoding>,
- url_data: UrlExtraData,
- stylesheet_loader: Option<&dyn StylesheetLoader>,
- error_reporter: Option<&dyn ParseErrorReporter>,
- ) {
- let string = decode_stylesheet_bytes(bytes, protocol_encoding_label, environment_encoding);
- Self::update_from_str(
- existing,
- &string,
- url_data,
- stylesheet_loader,
- error_reporter,
- 0,
- AllowImportRules::Yes,
- )
- }
-}
diff --git a/components/style/error_reporting.rs b/components/style/error_reporting.rs
deleted file mode 100644
index 258c7c44ef4..00000000000
--- a/components/style/error_reporting.rs
+++ /dev/null
@@ -1,283 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
-
-//! Types used to report parsing errors.
-
-#![deny(missing_docs)]
-
-use crate::selector_parser::SelectorImpl;
-use crate::stylesheets::UrlExtraData;
-use cssparser::{BasicParseErrorKind, ParseErrorKind, SourceLocation, Token};
-use selectors::SelectorList;
-use std::fmt;
-use style_traits::ParseError;
-
-/// Errors that can be encountered while parsing CSS.
-#[derive(Debug)]
-pub enum ContextualParseError<'a> {
- /// A property declaration was not recognized.
- UnsupportedPropertyDeclaration(
- &'a str,
- ParseError<'a>,
- Option<&'a SelectorList<SelectorImpl>>,
- ),
- /// A property descriptor was not recognized.
- UnsupportedPropertyDescriptor(&'a str, ParseError<'a>),
- /// A font face descriptor was not recognized.
- UnsupportedFontFaceDescriptor(&'a str, ParseError<'a>),
- /// A font feature values descriptor was not recognized.
- UnsupportedFontFeatureValuesDescriptor(&'a str, ParseError<'a>),
- /// A font palette values descriptor was not recognized.
- UnsupportedFontPaletteValuesDescriptor(&'a str, ParseError<'a>),
- /// A keyframe rule was not valid.
- InvalidKeyframeRule(&'a str, ParseError<'a>),
- /// A font feature values rule was not valid.
- InvalidFontFeatureValuesRule(&'a str, ParseError<'a>),
- /// A keyframe property declaration was not recognized.
- UnsupportedKeyframePropertyDeclaration(&'a str, ParseError<'a>),
- /// A rule was invalid for some reason.
- InvalidRule(&'a str, ParseError<'a>),
- /// A rule was not recognized.
- UnsupportedRule(&'a str, ParseError<'a>),
- /// A viewport descriptor declaration was not recognized.
- UnsupportedViewportDescriptorDeclaration(&'a str, ParseError<'a>),
- /// A counter style descriptor declaration was not recognized.
- UnsupportedCounterStyleDescriptorDeclaration(&'a str, ParseError<'a>),
- /// A counter style rule had no symbols.
- InvalidCounterStyleWithoutSymbols(String),
- /// A counter style rule had less than two symbols.
- InvalidCounterStyleNotEnoughSymbols(String),
- /// A counter style rule did not have additive-symbols.
- InvalidCounterStyleWithoutAdditiveSymbols,
- /// A counter style rule had extends with symbols.
- InvalidCounterStyleExtendsWithSymbols,
- /// A counter style rule had extends with additive-symbols.
- InvalidCounterStyleExtendsWithAdditiveSymbols,
- /// A media rule was invalid for some reason.
- InvalidMediaRule(&'a str, ParseError<'a>),
- /// A value was not recognized.
- UnsupportedValue(&'a str, ParseError<'a>),
- /// A never-matching `:host` selector was found.
- NeverMatchingHostSelector(String),
-}
-
-impl<'a> fmt::Display for ContextualParseError<'a> {
- fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
- fn token_to_str(t: &Token, f: &mut fmt::Formatter) -> fmt::Result {
- match *t {
- Token::Ident(ref i) => write!(f, "identifier {}", i),
- Token::AtKeyword(ref kw) => write!(f, "keyword @{}", kw),
- Token::Hash(ref h) => write!(f, "hash #{}", h),
- Token::IDHash(ref h) => write!(f, "id selector #{}", h),
- Token::QuotedString(ref s) => write!(f, "quoted string \"{}\"", s),
- Token::UnquotedUrl(ref u) => write!(f, "url {}", u),
- Token::Delim(ref d) => write!(f, "delimiter {}", d),
- Token::Number {
- int_value: Some(i), ..
- } => write!(f, "number {}", i),
- Token::Number { value, .. } => write!(f, "number {}", value),
- Token::Percentage {
- int_value: Some(i), ..
- } => write!(f, "percentage {}", i),
- Token::Percentage { unit_value, .. } => {
- write!(f, "percentage {}", unit_value * 100.)
- },
- Token::Dimension {
- value, ref unit, ..
- } => write!(f, "dimension {}{}", value, unit),
- Token::WhiteSpace(_) => write!(f, "whitespace"),
- Token::Comment(_) => write!(f, "comment"),
- Token::Colon => write!(f, "colon (:)"),
- Token::Semicolon => write!(f, "semicolon (;)"),
- Token::Comma => write!(f, "comma (,)"),
- Token::IncludeMatch => write!(f, "include match (~=)"),
- Token::DashMatch => write!(f, "dash match (|=)"),
- Token::PrefixMatch => write!(f, "prefix match (^=)"),
- Token::SuffixMatch => write!(f, "suffix match ($=)"),
- Token::SubstringMatch => write!(f, "substring match (*=)"),
- Token::CDO => write!(f, "CDO (<!--)"),
- Token::CDC => write!(f, "CDC (-->)"),
- Token::Function(ref name) => write!(f, "function {}", name),
- Token::ParenthesisBlock => write!(f, "parenthesis ("),
- Token::SquareBracketBlock => write!(f, "square bracket ["),
- Token::CurlyBracketBlock => write!(f, "curly bracket {{"),
- Token::BadUrl(ref _u) => write!(f, "bad url parse error"),
- Token::BadString(ref _s) => write!(f, "bad string parse error"),
- Token::CloseParenthesis => write!(f, "unmatched close parenthesis"),
- Token::CloseSquareBracket => write!(f, "unmatched close square bracket"),
- Token::CloseCurlyBracket => write!(f, "unmatched close curly bracket"),
- }
- }
-
- fn parse_error_to_str(err: &ParseError, f: &mut fmt::Formatter) -> fmt::Result {
- match err.kind {
- ParseErrorKind::Basic(BasicParseErrorKind::UnexpectedToken(ref t)) => {
- write!(f, "found unexpected ")?;
- token_to_str(t, f)
- },
- ParseErrorKind::Basic(BasicParseErrorKind::EndOfInput) => {
- write!(f, "unexpected end of input")
- },
- ParseErrorKind::Basic(BasicParseErrorKind::AtRuleInvalid(ref i)) => {
- write!(f, "@ rule invalid: {}", i)
- },
- ParseErrorKind::Basic(BasicParseErrorKind::AtRuleBodyInvalid) => {
- write!(f, "@ rule invalid")
- },
- ParseErrorKind::Basic(BasicParseErrorKind::QualifiedRuleInvalid) => {
- write!(f, "qualified rule invalid")
- },
- ParseErrorKind::Custom(ref err) => write!(f, "{:?}", err),
- }
- }
-
- match *self {
- ContextualParseError::UnsupportedPropertyDeclaration(decl, ref err, _selectors) => {
- write!(f, "Unsupported property declaration: '{}', ", decl)?;
- parse_error_to_str(err, f)
- },
- ContextualParseError::UnsupportedPropertyDescriptor(decl, ref err) => {
- write!(
- f,
- "Unsupported @property descriptor declaration: '{}', ",
- decl
- )?;
- parse_error_to_str(err, f)
- },
- ContextualParseError::UnsupportedFontFaceDescriptor(decl, ref err) => {
- write!(
- f,
- "Unsupported @font-face descriptor declaration: '{}', ",
- decl
- )?;
- parse_error_to_str(err, f)
- },
- ContextualParseError::UnsupportedFontFeatureValuesDescriptor(decl, ref err) => {
- write!(
- f,
- "Unsupported @font-feature-values descriptor declaration: '{}', ",
- decl
- )?;
- parse_error_to_str(err, f)
- },
- ContextualParseError::UnsupportedFontPaletteValuesDescriptor(decl, ref err) => {
- write!(
- f,
- "Unsupported @font-palette-values descriptor declaration: '{}', ",
- decl
- )?;
- parse_error_to_str(err, f)
- },
- ContextualParseError::InvalidKeyframeRule(rule, ref err) => {
- write!(f, "Invalid keyframe rule: '{}', ", rule)?;
- parse_error_to_str(err, f)
- },
- ContextualParseError::InvalidFontFeatureValuesRule(rule, ref err) => {
- write!(f, "Invalid font feature value rule: '{}', ", rule)?;
- parse_error_to_str(err, f)
- },
- ContextualParseError::UnsupportedKeyframePropertyDeclaration(decl, ref err) => {
- write!(f, "Unsupported keyframe property declaration: '{}', ", decl)?;
- parse_error_to_str(err, f)
- },
- ContextualParseError::InvalidRule(rule, ref err) => {
- write!(f, "Invalid rule: '{}', ", rule)?;
- parse_error_to_str(err, f)
- },
- ContextualParseError::UnsupportedRule(rule, ref err) => {
- write!(f, "Unsupported rule: '{}', ", rule)?;
- parse_error_to_str(err, f)
- },
- ContextualParseError::UnsupportedViewportDescriptorDeclaration(decl, ref err) => {
- write!(
- f,
- "Unsupported @viewport descriptor declaration: '{}', ",
- decl
- )?;
- parse_error_to_str(err, f)
- },
- ContextualParseError::UnsupportedCounterStyleDescriptorDeclaration(decl, ref err) => {
- write!(
- f,
- "Unsupported @counter-style descriptor declaration: '{}', ",
- decl
- )?;
- parse_error_to_str(err, f)
- },
- ContextualParseError::InvalidCounterStyleWithoutSymbols(ref system) => write!(
- f,
- "Invalid @counter-style rule: 'system: {}' without 'symbols'",
- system
- ),
- ContextualParseError::InvalidCounterStyleNotEnoughSymbols(ref system) => write!(
- f,
- "Invalid @counter-style rule: 'system: {}' less than two 'symbols'",
- system
- ),
- ContextualParseError::InvalidCounterStyleWithoutAdditiveSymbols => write!(
- f,
- "Invalid @counter-style rule: 'system: additive' without 'additive-symbols'"
- ),
- ContextualParseError::InvalidCounterStyleExtendsWithSymbols => write!(
- f,
- "Invalid @counter-style rule: 'system: extends …' with 'symbols'"
- ),
- ContextualParseError::InvalidCounterStyleExtendsWithAdditiveSymbols => write!(
- f,
- "Invalid @counter-style rule: 'system: extends …' with 'additive-symbols'"
- ),
- ContextualParseError::InvalidMediaRule(media_rule, ref err) => {
- write!(f, "Invalid media rule: {}, ", media_rule)?;
- parse_error_to_str(err, f)
- },
- ContextualParseError::UnsupportedValue(_value, ref err) => parse_error_to_str(err, f),
- ContextualParseError::NeverMatchingHostSelector(ref selector) => {
- write!(f, ":host selector is not featureless: {}", selector)
- },
- }
- }
-}
-
-/// A generic trait for an error reporter.
-pub trait ParseErrorReporter {
- /// Called when the style engine detects an error.
- ///
- /// Returns the current input being parsed, the source location it was
- /// reported from, and a message.
- fn report_error(
- &self,
- url: &UrlExtraData,
- location: SourceLocation,
- error: ContextualParseError,
- );
-}
-
-/// An error reporter that uses [the `log` crate](https://github.com/rust-lang-nursery/log)
-/// at `info` level.
-///
-/// This logging is silent by default, and can be enabled with a `RUST_LOG=style=info`
-/// environment variable.
-/// (See [`env_logger`](https://rust-lang-nursery.github.io/log/env_logger/).)
-#[cfg(feature = "servo")]
-pub struct RustLogReporter;
-
-#[cfg(feature = "servo")]
-impl ParseErrorReporter for RustLogReporter {
- fn report_error(
- &self,
- url: &UrlExtraData,
- location: SourceLocation,
- error: ContextualParseError,
- ) {
- if log_enabled!(log::Level::Info) {
- info!(
- "Url:\t{}\n{}:{} {}",
- url.as_str(),
- location.line,
- location.column,
- error
- )
- }
- }
-}
diff --git a/components/style/font_face.rs b/components/style/font_face.rs
deleted file mode 100644
index 1e193d3eb79..00000000000
--- a/components/style/font_face.rs
+++ /dev/null
@@ -1,835 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
-
-//! The [`@font-face`][ff] at-rule.
-//!
-//! [ff]: https://drafts.csswg.org/css-fonts/#at-font-face-rule
-
-use crate::error_reporting::ContextualParseError;
-use crate::parser::{Parse, ParserContext};
-#[cfg(feature = "gecko")]
-use crate::properties::longhands::font_language_override;
-use crate::shared_lock::{SharedRwLockReadGuard, ToCssWithGuard};
-use crate::str::CssStringWriter;
-use crate::values::computed::font::{FamilyName, FontStretch};
-use crate::values::generics::font::FontStyle as GenericFontStyle;
-#[cfg(feature = "gecko")]
-use crate::values::specified::font::MetricsOverride;
-use crate::values::specified::font::SpecifiedFontStyle;
-use crate::values::specified::font::{AbsoluteFontWeight, FontStretch as SpecifiedFontStretch};
-#[cfg(feature = "gecko")]
-use crate::values::specified::font::{FontFeatureSettings, FontVariationSettings};
-use crate::values::specified::url::SpecifiedUrl;
-use crate::values::specified::Angle;
-#[cfg(feature = "gecko")]
-use crate::values::specified::NonNegativePercentage;
-#[cfg(feature = "gecko")]
-use cssparser::UnicodeRange;
-use cssparser::{
- AtRuleParser, CowRcStr, DeclarationParser, Parser, QualifiedRuleParser, RuleBodyItemParser,
- RuleBodyParser, SourceLocation,
-};
-use selectors::parser::SelectorParseErrorKind;
-use std::fmt::{self, Write};
-use style_traits::{CssWriter, ParseError};
-use style_traits::{StyleParseErrorKind, ToCss};
-
-/// A source for a font-face rule.
-#[cfg_attr(feature = "servo", derive(Deserialize, Serialize))]
-#[derive(Clone, Debug, Eq, PartialEq, ToCss, ToShmem)]
-pub enum Source {
- /// A `url()` source.
- Url(UrlSource),
- /// A `local()` source.
- #[css(function)]
- Local(FamilyName),
-}
-
-/// A list of sources for the font-face src descriptor.
-#[derive(Clone, Debug, Eq, PartialEq, ToCss, ToShmem)]
-#[css(comma)]
-pub struct SourceList(#[css(iterable)] pub Vec<Source>);
-
-// We can't just use OneOrMoreSeparated to derive Parse for the Source list,
-// because we want to filter out components that parsed as None, then fail if no
-// valid components remain. So we provide our own implementation here.
-impl Parse for SourceList {
- fn parse<'i, 't>(
- context: &ParserContext,
- input: &mut Parser<'i, 't>,
- ) -> Result<Self, ParseError<'i>> {
- // Parse the comma-separated list, then let filter_map discard any None items.
- let list = input
- .parse_comma_separated(|input| {
- let s = input.parse_entirely(|input| Source::parse(context, input));
- while input.next().is_ok() {}
- Ok(s.ok())
- })?
- .into_iter()
- .filter_map(|s| s)
- .collect::<Vec<Source>>();
- if list.is_empty() {
- Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError))
- } else {
- Ok(SourceList(list))
- }
- }
-}
-
-/// Keywords for the font-face src descriptor's format() function.
-/// ('None' and 'Unknown' are for internal use in gfx, not exposed to CSS.)
-#[derive(Clone, Copy, Debug, Eq, Parse, PartialEq, ToCss, ToShmem)]
-#[cfg_attr(feature = "servo", derive(Deserialize, Serialize))]
-#[repr(u8)]
-#[allow(missing_docs)]
-pub enum FontFaceSourceFormatKeyword {
- #[css(skip)]
- None,
- Collection,
- EmbeddedOpentype,
- Opentype,
- Svg,
- Truetype,
- Woff,
- Woff2,
- #[css(skip)]
- Unknown,
-}
-
-bitflags! {
- /// Flags for the @font-face tech() function, indicating font technologies
- /// required by the resource.
- #[derive(ToShmem)]
- #[cfg_attr(feature = "servo", derive(Deserialize, Serialize))]
- #[repr(C)]
- pub struct FontFaceSourceTechFlags: u16 {
- /// Font requires OpenType feature support.
- const FEATURES_OPENTYPE = 1 << 0;
- /// Font requires Apple Advanced Typography support.
- const FEATURES_AAT = 1 << 1;
- /// Font requires Graphite shaping support.
- const FEATURES_GRAPHITE = 1 << 2;
- /// Font requires COLRv0 rendering support (simple list of colored layers).
- const COLOR_COLRV0 = 1 << 3;
- /// Font requires COLRv1 rendering support (graph of paint operations).
- const COLOR_COLRV1 = 1 << 4;
- /// Font requires SVG glyph rendering support.
- const COLOR_SVG = 1 << 5;
- /// Font has bitmap glyphs in 'sbix' format.
- const COLOR_SBIX = 1 << 6;
- /// Font has bitmap glyphs in 'CBDT' format.
- const COLOR_CBDT = 1 << 7;
- /// Font requires OpenType Variations support.
- const VARIATIONS = 1 << 8;
- /// Font requires CPAL palette selection support.
- const PALETTES = 1 << 9;
- /// Font requires support for incremental downloading.
- const INCREMENTAL = 1 << 10;
- }
-}
-
-impl FontFaceSourceTechFlags {
- /// Parse a single font-technology keyword and return its flag.
- pub fn parse_one<'i, 't>(input: &mut Parser<'i, 't>) -> Result<Self, ParseError<'i>> {
- Ok(try_match_ident_ignore_ascii_case! { input,
- "features-opentype" => Self::FEATURES_OPENTYPE,
- "features-aat" => Self::FEATURES_AAT,
- "features-graphite" => Self::FEATURES_GRAPHITE,
- "color-colrv0" => Self::COLOR_COLRV0,
- "color-colrv1" => Self::COLOR_COLRV1,
- "color-svg" => Self::COLOR_SVG,
- "color-sbix" => Self::COLOR_SBIX,
- "color-cbdt" => Self::COLOR_CBDT,
- "variations" => Self::VARIATIONS,
- "palettes" => Self::PALETTES,
- "incremental" => Self::INCREMENTAL,
- })
- }
-}
-
-impl Parse for FontFaceSourceTechFlags {
- fn parse<'i, 't>(
- _context: &ParserContext,
- input: &mut Parser<'i, 't>,
- ) -> Result<Self, ParseError<'i>> {
- let location = input.current_source_location();
- // We don't actually care about the return value of parse_comma_separated,
- // because we insert the flags into result as we go.
- let mut result = Self::empty();
- input.parse_comma_separated(|input| {
- let flag = Self::parse_one(input)?;
- result.insert(flag);
- Ok(())
- })?;
- if !result.is_empty() {
- Ok(result)
- } else {
- Err(location.new_custom_error(StyleParseErrorKind::UnspecifiedError))
- }
- }
-}
-
-#[allow(unused_assignments)]
-impl ToCss for FontFaceSourceTechFlags {
- fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
- where
- W: fmt::Write,
- {
- let mut first = true;
-
- macro_rules! write_if_flag {
- ($s:expr => $f:ident) => {
- if self.contains(Self::$f) {
- if first {
- first = false;
- } else {
- dest.write_str(", ")?;
- }
- dest.write_str($s)?;
- }
- };
- }
-
- write_if_flag!("features-opentype" => FEATURES_OPENTYPE);
- write_if_flag!("features-aat" => FEATURES_AAT);
- write_if_flag!("features-graphite" => FEATURES_GRAPHITE);
- write_if_flag!("color-colrv0" => COLOR_COLRV0);
- write_if_flag!("color-colrv1" => COLOR_COLRV1);
- write_if_flag!("color-svg" => COLOR_SVG);
- write_if_flag!("color-sbix" => COLOR_SBIX);
- write_if_flag!("color-cbdt" => COLOR_CBDT);
- write_if_flag!("variations" => VARIATIONS);
- write_if_flag!("palettes" => PALETTES);
- write_if_flag!("incremental" => INCREMENTAL);
-
- Ok(())
- }
-}
-
-/// A POD representation for Gecko. All pointers here are non-owned and as such
-/// can't outlive the rule they came from, but we can't enforce that via C++.
-///
-/// All the strings are of course utf8.
-#[cfg(feature = "gecko")]
-#[derive(Clone, Copy, Debug, Eq, PartialEq)]
-#[repr(u8)]
-#[allow(missing_docs)]
-pub enum FontFaceSourceListComponent {
- Url(*const crate::gecko::url::CssUrl),
- Local(*mut crate::gecko_bindings::structs::nsAtom),
- FormatHintKeyword(FontFaceSourceFormatKeyword),
- FormatHintString {
- length: usize,
- utf8_bytes: *const u8,
- },
- TechFlags(FontFaceSourceTechFlags),
-}
-
-#[derive(Clone, Debug, Eq, PartialEq, ToCss, ToShmem)]
-#[cfg_attr(feature = "servo", derive(Deserialize, Serialize))]
-#[repr(u8)]
-#[allow(missing_docs)]
-pub enum FontFaceSourceFormat {
- Keyword(FontFaceSourceFormatKeyword),
- String(String),
-}
-
-/// A `UrlSource` represents a font-face source that has been specified with a
-/// `url()` function.
-///
-/// <https://drafts.csswg.org/css-fonts/#src-desc>
-#[cfg_attr(feature = "servo", derive(Deserialize, Serialize))]
-#[derive(Clone, Debug, Eq, PartialEq, ToShmem)]
-pub struct UrlSource {
- /// The specified url.
- pub url: SpecifiedUrl,
- /// The format hint specified with the `format()` function, if present.
- pub format_hint: Option<FontFaceSourceFormat>,
- /// The font technology flags specified with the `tech()` function, if any.
- pub tech_flags: FontFaceSourceTechFlags,
-}
-
-impl ToCss for UrlSource {
- fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
- where
- W: fmt::Write,
- {
- self.url.to_css(dest)?;
- if let Some(hint) = &self.format_hint {
- dest.write_str(" format(")?;
- hint.to_css(dest)?;
- dest.write_char(')')?;
- }
- if !self.tech_flags.is_empty() {
- dest.write_str(" tech(")?;
- self.tech_flags.to_css(dest)?;
- dest.write_char(')')?;
- }
- Ok(())
- }
-}
-
-/// A font-display value for a @font-face rule.
-/// The font-display descriptor determines how a font face is displayed based
-/// on whether and when it is downloaded and ready to use.
-#[allow(missing_docs)]
-#[cfg_attr(feature = "servo", derive(Deserialize, Serialize))]
-#[derive(
- Clone, Copy, Debug, Eq, MallocSizeOf, Parse, PartialEq, ToComputedValue, ToCss, ToShmem,
-)]
-#[repr(u8)]
-pub enum FontDisplay {
- Auto,
- Block,
- Swap,
- Fallback,
- Optional,
-}
-
-macro_rules! impl_range {
- ($range:ident, $component:ident) => {
- impl Parse for $range {
- fn parse<'i, 't>(
- context: &ParserContext,
- input: &mut Parser<'i, 't>,
- ) -> Result<Self, ParseError<'i>> {
- let first = $component::parse(context, input)?;
- let second = input
- .try_parse(|input| $component::parse(context, input))
- .unwrap_or_else(|_| first.clone());
- Ok($range(first, second))
- }
- }
- impl ToCss for $range {
- fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
- where
- W: fmt::Write,
- {
- self.0.to_css(dest)?;
- if self.0 != self.1 {
- dest.write_char(' ')?;
- self.1.to_css(dest)?;
- }
- Ok(())
- }
- }
- };
-}
-
-/// The font-weight descriptor:
-///
-/// https://drafts.csswg.org/css-fonts-4/#descdef-font-face-font-weight
-#[derive(Clone, Debug, PartialEq, ToShmem)]
-pub struct FontWeightRange(pub AbsoluteFontWeight, pub AbsoluteFontWeight);
-impl_range!(FontWeightRange, AbsoluteFontWeight);
-
-/// The computed representation of the above so Gecko can read them easily.
-///
-/// This one is needed because cbindgen doesn't know how to generate
-/// specified::Number.
-#[repr(C)]
-#[allow(missing_docs)]
-pub struct ComputedFontWeightRange(f32, f32);
-
-#[inline]
-fn sort_range<T: PartialOrd>(a: T, b: T) -> (T, T) {
- if a > b {
- (b, a)
- } else {
- (a, b)
- }
-}
-
-impl FontWeightRange {
- /// Returns a computed font-stretch range.
- pub fn compute(&self) -> ComputedFontWeightRange {
- let (min, max) = sort_range(self.0.compute().value(), self.1.compute().value());
- ComputedFontWeightRange(min, max)
- }
-}
-
-/// The font-stretch descriptor:
-///
-/// https://drafts.csswg.org/css-fonts-4/#descdef-font-face-font-stretch
-#[derive(Clone, Debug, PartialEq, ToShmem)]
-pub struct FontStretchRange(pub SpecifiedFontStretch, pub SpecifiedFontStretch);
-impl_range!(FontStretchRange, SpecifiedFontStretch);
-
-/// The computed representation of the above, so that Gecko can read them
-/// easily.
-#[repr(C)]
-#[allow(missing_docs)]
-pub struct ComputedFontStretchRange(FontStretch, FontStretch);
-
-impl FontStretchRange {
- /// Returns a computed font-stretch range.
- pub fn compute(&self) -> ComputedFontStretchRange {
- fn compute_stretch(s: &SpecifiedFontStretch) -> FontStretch {
- match *s {
- SpecifiedFontStretch::Keyword(ref kw) => kw.compute(),
- SpecifiedFontStretch::Stretch(ref p) => FontStretch::from_percentage(p.0.get()),
- SpecifiedFontStretch::System(..) => unreachable!(),
- }
- }
-
- let (min, max) = sort_range(compute_stretch(&self.0), compute_stretch(&self.1));
- ComputedFontStretchRange(min, max)
- }
-}
-
-/// The font-style descriptor:
-///
-/// https://drafts.csswg.org/css-fonts-4/#descdef-font-face-font-style
-#[derive(Clone, Debug, PartialEq, ToShmem)]
-#[allow(missing_docs)]
-pub enum FontStyle {
- Normal,
- Italic,
- Oblique(Angle, Angle),
-}
-
-/// The computed representation of the above, with angles in degrees, so that
-/// Gecko can read them easily.
-#[repr(u8)]
-#[allow(missing_docs)]
-pub enum ComputedFontStyleDescriptor {
- Normal,
- Italic,
- Oblique(f32, f32),
-}
-
-impl Parse for FontStyle {
- fn parse<'i, 't>(
- context: &ParserContext,
- input: &mut Parser<'i, 't>,
- ) -> Result<Self, ParseError<'i>> {
- let style = SpecifiedFontStyle::parse(context, input)?;
- Ok(match style {
- GenericFontStyle::Normal => FontStyle::Normal,
- GenericFontStyle::Italic => FontStyle::Italic,
- GenericFontStyle::Oblique(angle) => {
- let second_angle = input
- .try_parse(|input| SpecifiedFontStyle::parse_angle(context, input))
- .unwrap_or_else(|_| angle.clone());
-
- FontStyle::Oblique(angle, second_angle)
- },
- })
- }
-}
-
-impl ToCss for FontStyle {
- fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
- where
- W: fmt::Write,
- {
- match *self {
- FontStyle::Normal => dest.write_str("normal"),
- FontStyle::Italic => dest.write_str("italic"),
- FontStyle::Oblique(ref first, ref second) => {
- dest.write_str("oblique")?;
- if *first != SpecifiedFontStyle::default_angle() || first != second {
- dest.write_char(' ')?;
- first.to_css(dest)?;
- }
- if first != second {
- dest.write_char(' ')?;
- second.to_css(dest)?;
- }
- Ok(())
- },
- }
- }
-}
-
-impl FontStyle {
- /// Returns a computed font-style descriptor.
- pub fn compute(&self) -> ComputedFontStyleDescriptor {
- match *self {
- FontStyle::Normal => ComputedFontStyleDescriptor::Normal,
- FontStyle::Italic => ComputedFontStyleDescriptor::Italic,
- FontStyle::Oblique(ref first, ref second) => {
- let (min, max) = sort_range(
- SpecifiedFontStyle::compute_angle_degrees(first),
- SpecifiedFontStyle::compute_angle_degrees(second),
- );
- ComputedFontStyleDescriptor::Oblique(min, max)
- },
- }
- }
-}
-
-/// Parse the block inside a `@font-face` rule.
-///
-/// Note that the prelude parsing code lives in the `stylesheets` module.
-pub fn parse_font_face_block(
- context: &ParserContext,
- input: &mut Parser,
- location: SourceLocation,
-) -> FontFaceRuleData {
- let mut rule = FontFaceRuleData::empty(location);
- {
- let mut parser = FontFaceRuleParser {
- context,
- rule: &mut rule,
- };
- let mut iter = RuleBodyParser::new(input, &mut parser);
- while let Some(declaration) = iter.next() {
- if let Err((error, slice)) = declaration {
- let location = error.location;
- let error = ContextualParseError::UnsupportedFontFaceDescriptor(slice, error);
- context.log_css_error(location, error)
- }
- }
- }
- rule
-}
-
-/// A @font-face rule that is known to have font-family and src declarations.
-#[cfg(feature = "servo")]
-pub struct FontFace<'a>(&'a FontFaceRuleData);
-
-/// A list of effective sources that we send over through IPC to the font cache.
-#[cfg(feature = "servo")]
-#[derive(Clone, Debug)]
-#[cfg_attr(feature = "servo", derive(Deserialize, Serialize))]
-pub struct EffectiveSources(Vec<Source>);
-
-#[cfg(feature = "servo")]
-impl<'a> FontFace<'a> {
- /// Returns the list of effective sources for that font-face, that is the
- /// sources which don't list any format hint, or the ones which list at
- /// least "truetype" or "opentype".
- pub fn effective_sources(&self) -> EffectiveSources {
- EffectiveSources(
- self.sources()
- .0
- .iter()
- .rev()
- .filter(|source| {
- if let Source::Url(ref url_source) = **source {
- // We support only opentype fonts and truetype is an alias for
- // that format. Sources without format hints need to be
- // downloaded in case we support them.
- url_source
- .format_hint
- .as_ref()
- .map_or(true, |hint| match hint {
- FontFaceSourceFormat::Keyword(
- FontFaceSourceFormatKeyword::Truetype
- | FontFaceSourceFormatKeyword::Opentype
- | FontFaceSourceFormatKeyword::Woff,
- ) => true,
- FontFaceSourceFormat::String(s) => {
- s == "truetype" || s == "opentype" || s == "woff"
- }
- _ => false,
- })
- } else {
- true
- }
- })
- .cloned()
- .collect(),
- )
- }
-}
-
-#[cfg(feature = "servo")]
-impl Iterator for EffectiveSources {
- type Item = Source;
- fn next(&mut self) -> Option<Source> {
- self.0.pop()
- }
-
- fn size_hint(&self) -> (usize, Option<usize>) {
- (self.0.len(), Some(self.0.len()))
- }
-}
-
-struct FontFaceRuleParser<'a, 'b: 'a> {
- context: &'a ParserContext<'b>,
- rule: &'a mut FontFaceRuleData,
-}
-
-/// Default methods reject all at rules.
-impl<'a, 'b, 'i> AtRuleParser<'i> for FontFaceRuleParser<'a, 'b> {
- type Prelude = ();
- type AtRule = ();
- type Error = StyleParseErrorKind<'i>;
-}
-
-impl<'a, 'b, 'i> QualifiedRuleParser<'i> for FontFaceRuleParser<'a, 'b> {
- type Prelude = ();
- type QualifiedRule = ();
- type Error = StyleParseErrorKind<'i>;
-}
-
-impl<'a, 'b, 'i> RuleBodyItemParser<'i, (), StyleParseErrorKind<'i>>
- for FontFaceRuleParser<'a, 'b>
-{
- fn parse_qualified(&self) -> bool {
- false
- }
- fn parse_declarations(&self) -> bool {
- true
- }
-}
-
-impl Parse for Source {
- fn parse<'i, 't>(
- context: &ParserContext,
- input: &mut Parser<'i, 't>,
- ) -> Result<Source, ParseError<'i>> {
- if input
- .try_parse(|input| input.expect_function_matching("local"))
- .is_ok()
- {
- return input
- .parse_nested_block(|input| FamilyName::parse(context, input))
- .map(Source::Local);
- }
-
- let url = SpecifiedUrl::parse(context, input)?;
-
- // Parsing optional format()
- let format_hint = if input
- .try_parse(|input| input.expect_function_matching("format"))
- .is_ok()
- {
- input.parse_nested_block(|input| {
- if let Ok(kw) = input.try_parse(FontFaceSourceFormatKeyword::parse) {
- Ok(Some(FontFaceSourceFormat::Keyword(kw)))
- } else {
- let s = input.expect_string()?.as_ref().to_owned();
- Ok(Some(FontFaceSourceFormat::String(s)))
- }
- })?
- } else {
- None
- };
-
- // Parse optional tech()
- let tech_flags = if static_prefs::pref!("layout.css.font-tech.enabled") &&
- input
- .try_parse(|input| input.expect_function_matching("tech"))
- .is_ok()
- {
- input.parse_nested_block(|input| FontFaceSourceTechFlags::parse(context, input))?
- } else {
- FontFaceSourceTechFlags::empty()
- };
-
- Ok(Source::Url(UrlSource {
- url,
- format_hint,
- tech_flags,
- }))
- }
-}
-
-macro_rules! is_descriptor_enabled {
- ("font-display") => {
- static_prefs::pref!("layout.css.font-display.enabled")
- };
- ("font-variation-settings") => {
- static_prefs::pref!("layout.css.font-variations.enabled")
- };
- ("ascent-override") => {
- static_prefs::pref!("layout.css.font-metrics-overrides.enabled")
- };
- ("descent-override") => {
- static_prefs::pref!("layout.css.font-metrics-overrides.enabled")
- };
- ("line-gap-override") => {
- static_prefs::pref!("layout.css.font-metrics-overrides.enabled")
- };
- ("size-adjust") => {
- static_prefs::pref!("layout.css.size-adjust.enabled")
- };
- ($name:tt) => {
- true
- };
-}
-
-macro_rules! font_face_descriptors_common {
- (
- $( #[$doc: meta] $name: tt $ident: ident / $gecko_ident: ident: $ty: ty, )*
- ) => {
- /// Data inside a `@font-face` rule.
- ///
- /// <https://drafts.csswg.org/css-fonts/#font-face-rule>
- #[derive(Clone, Debug, PartialEq, ToShmem)]
- pub struct FontFaceRuleData {
- $(
- #[$doc]
- pub $ident: Option<$ty>,
- )*
- /// Line and column of the @font-face rule source code.
- pub source_location: SourceLocation,
- }
-
- impl FontFaceRuleData {
- /// Create an empty font-face rule
- pub fn empty(location: SourceLocation) -> Self {
- FontFaceRuleData {
- $(
- $ident: None,
- )*
- source_location: location,
- }
- }
-
- /// Serialization of declarations in the FontFaceRule
- pub fn decl_to_css(&self, dest: &mut CssStringWriter) -> fmt::Result {
- $(
- if let Some(ref value) = self.$ident {
- dest.write_str(concat!($name, ": "))?;
- value.to_css(&mut CssWriter::new(dest))?;
- dest.write_str("; ")?;
- }
- )*
- Ok(())
- }
- }
-
- impl<'a, 'b, 'i> DeclarationParser<'i> for FontFaceRuleParser<'a, 'b> {
- type Declaration = ();
- type Error = StyleParseErrorKind<'i>;
-
- fn parse_value<'t>(
- &mut self,
- name: CowRcStr<'i>,
- input: &mut Parser<'i, 't>,
- ) -> Result<(), ParseError<'i>> {
- match_ignore_ascii_case! { &*name,
- $(
- $name if is_descriptor_enabled!($name) => {
- // DeclarationParser also calls parse_entirely
- // so we’d normally not need to,
- // but in this case we do because we set the value as a side effect
- // rather than returning it.
- let value = input.parse_entirely(|i| Parse::parse(self.context, i))?;
- self.rule.$ident = Some(value)
- },
- )*
- _ => return Err(input.new_custom_error(SelectorParseErrorKind::UnexpectedIdent(name.clone()))),
- }
- Ok(())
- }
- }
- }
-}
-
-impl ToCssWithGuard for FontFaceRuleData {
- // Serialization of FontFaceRule is not specced.
- fn to_css(&self, _guard: &SharedRwLockReadGuard, dest: &mut CssStringWriter) -> fmt::Result {
- dest.write_str("@font-face { ")?;
- self.decl_to_css(dest)?;
- dest.write_char('}')
- }
-}
-
-macro_rules! font_face_descriptors {
- (
- mandatory descriptors = [
- $( #[$m_doc: meta] $m_name: tt $m_ident: ident / $m_gecko_ident: ident: $m_ty: ty, )*
- ]
- optional descriptors = [
- $( #[$o_doc: meta] $o_name: tt $o_ident: ident / $o_gecko_ident: ident: $o_ty: ty, )*
- ]
- ) => {
- font_face_descriptors_common! {
- $( #[$m_doc] $m_name $m_ident / $m_gecko_ident: $m_ty, )*
- $( #[$o_doc] $o_name $o_ident / $o_gecko_ident: $o_ty, )*
- }
-
- impl FontFaceRuleData {
- /// Per https://github.com/w3c/csswg-drafts/issues/1133 an @font-face rule
- /// is valid as far as the CSS parser is concerned even if it doesn’t have
- /// a font-family or src declaration.
- ///
- /// However both are required for the rule to represent an actual font face.
- #[cfg(feature = "servo")]
- pub fn font_face(&self) -> Option<FontFace> {
- if $( self.$m_ident.is_some() )&&* {
- Some(FontFace(self))
- } else {
- None
- }
- }
- }
-
- #[cfg(feature = "servo")]
- impl<'a> FontFace<'a> {
- $(
- #[$m_doc]
- pub fn $m_ident(&self) -> &$m_ty {
- self.0 .$m_ident.as_ref().unwrap()
- }
- )*
- }
- }
-}
-
-#[cfg(feature = "gecko")]
-font_face_descriptors! {
- mandatory descriptors = [
- /// The name of this font face
- "font-family" family / mFamily: FamilyName,
-
- /// The alternative sources for this font face.
- "src" sources / mSrc: SourceList,
- ]
- optional descriptors = [
- /// The style of this font face.
- "font-style" style / mStyle: FontStyle,
-
- /// The weight of this font face.
- "font-weight" weight / mWeight: FontWeightRange,
-
- /// The stretch of this font face.
- "font-stretch" stretch / mStretch: FontStretchRange,
-
- /// The display of this font face.
- "font-display" display / mDisplay: FontDisplay,
-
- /// The ranges of code points outside of which this font face should not be used.
- "unicode-range" unicode_range / mUnicodeRange: Vec<UnicodeRange>,
-
- /// The feature settings of this font face.
- "font-feature-settings" feature_settings / mFontFeatureSettings: FontFeatureSettings,
-
- /// The variation settings of this font face.
- "font-variation-settings" variation_settings / mFontVariationSettings: FontVariationSettings,
-
- /// The language override of this font face.
- "font-language-override" language_override / mFontLanguageOverride: font_language_override::SpecifiedValue,
-
- /// The ascent override for this font face.
- "ascent-override" ascent_override / mAscentOverride: MetricsOverride,
-
- /// The descent override for this font face.
- "descent-override" descent_override / mDescentOverride: MetricsOverride,
-
- /// The line-gap override for this font face.
- "line-gap-override" line_gap_override / mLineGapOverride: MetricsOverride,
-
- /// The size adjustment for this font face.
- "size-adjust" size_adjust / mSizeAdjust: NonNegativePercentage,
- ]
-}
-
-#[cfg(feature = "servo")]
-font_face_descriptors! {
- mandatory descriptors = [
- /// The name of this font face
- "font-family" family / mFamily: FamilyName,
-
- /// The alternative sources for this font face.
- "src" sources / mSrc: SourceList,
- ]
- optional descriptors = [
- ]
-}
diff --git a/components/style/font_metrics.rs b/components/style/font_metrics.rs
deleted file mode 100644
index 391d3653ee6..00000000000
--- a/components/style/font_metrics.rs
+++ /dev/null
@@ -1,58 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
-
-//! Access to font metrics from the style system.
-
-#![deny(missing_docs)]
-
-use crate::values::computed::Length;
-
-/// Represents the font metrics that style needs from a font to compute the
-/// value of certain CSS units like `ex`.
-#[derive(Clone, Debug, PartialEq)]
-pub struct FontMetrics {
- /// The x-height of the font.
- pub x_height: Option<Length>,
- /// The zero advance. This is usually writing mode dependent
- pub zero_advance_measure: Option<Length>,
- /// The cap-height of the font.
- pub cap_height: Option<Length>,
- /// The ideographic-width of the font.
- pub ic_width: Option<Length>,
- /// The ascent of the font (a value is always available for this).
- pub ascent: Length,
- /// Script scale down factor for math-depth 1.
- /// https://w3c.github.io/mathml-core/#dfn-scriptpercentscaledown
- pub script_percent_scale_down: Option<f32>,
- /// Script scale down factor for math-depth 2.
- /// https://w3c.github.io/mathml-core/#dfn-scriptscriptpercentscaledown
- pub script_script_percent_scale_down: Option<f32>,
-}
-
-impl Default for FontMetrics {
- fn default() -> Self {
- FontMetrics {
- x_height: None,
- zero_advance_measure: None,
- cap_height: None,
- ic_width: None,
- ascent: Length::new(0.0),
- script_percent_scale_down: None,
- script_script_percent_scale_down: None,
- }
- }
-}
-
-/// Type of font metrics to retrieve.
-#[derive(Clone, Debug, PartialEq)]
-pub enum FontMetricsOrientation {
- /// Get metrics for horizontal or vertical according to the Context's
- /// writing mode, using horizontal metrics for vertical/mixed
- MatchContextPreferHorizontal,
- /// Get metrics for horizontal or vertical according to the Context's
- /// writing mode, using vertical metrics for vertical/mixed
- MatchContextPreferVertical,
- /// Force getting horizontal metrics.
- Horizontal,
-}
diff --git a/components/style/gecko/arc_types.rs b/components/style/gecko/arc_types.rs
deleted file mode 100644
index 24bf22d69a7..00000000000
--- a/components/style/gecko/arc_types.rs
+++ /dev/null
@@ -1,171 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
-
-//! This file lists all arc FFI types and defines corresponding addref and release functions. This
-//! list loosely corresponds to ServoLockedArcTypeList.h file in Gecko.
-
-#![allow(non_snake_case, missing_docs)]
-
-use crate::gecko::url::CssUrlData;
-use crate::media_queries::MediaList;
-use crate::properties::animated_properties::AnimationValue;
-use crate::properties::{ComputedValues, PropertyDeclarationBlock};
-use crate::shared_lock::Locked;
-use crate::stylesheets::keyframes_rule::Keyframe;
-use crate::stylesheets::{
- ContainerRule, CounterStyleRule, CssRules, DocumentRule, FontFaceRule, FontFeatureValuesRule,
- FontPaletteValuesRule, ImportRule, KeyframesRule, LayerBlockRule, LayerStatementRule,
- MediaRule, NamespaceRule, PageRule, PropertyRule, StyleRule, StylesheetContents, SupportsRule,
-};
-use servo_arc::Arc;
-
-macro_rules! impl_simple_arc_ffi {
- ($ty:ty, $addref:ident, $release:ident) => {
- #[no_mangle]
- pub unsafe extern "C" fn $addref(obj: *const $ty) {
- std::mem::forget(Arc::from_raw_addrefed(obj));
- }
-
- #[no_mangle]
- pub unsafe extern "C" fn $release(obj: *const $ty) {
- let _ = Arc::from_raw(obj);
- }
- };
-}
-
-macro_rules! impl_locked_arc_ffi {
- ($servo_type:ty, $alias:ident, $addref:ident, $release:ident) => {
- /// A simple alias for a locked type.
- pub type $alias = Locked<$servo_type>;
- impl_simple_arc_ffi!($alias, $addref, $release);
- };
-}
-
-impl_locked_arc_ffi!(
- CssRules,
- LockedCssRules,
- Servo_CssRules_AddRef,
- Servo_CssRules_Release
-);
-impl_locked_arc_ffi!(
- PropertyDeclarationBlock,
- LockedDeclarationBlock,
- Servo_DeclarationBlock_AddRef,
- Servo_DeclarationBlock_Release
-);
-impl_locked_arc_ffi!(
- StyleRule,
- LockedStyleRule,
- Servo_StyleRule_AddRef,
- Servo_StyleRule_Release
-);
-impl_locked_arc_ffi!(
- ImportRule,
- LockedImportRule,
- Servo_ImportRule_AddRef,
- Servo_ImportRule_Release
-);
-impl_locked_arc_ffi!(
- Keyframe,
- LockedKeyframe,
- Servo_Keyframe_AddRef,
- Servo_Keyframe_Release
-);
-impl_locked_arc_ffi!(
- KeyframesRule,
- LockedKeyframesRule,
- Servo_KeyframesRule_AddRef,
- Servo_KeyframesRule_Release
-);
-impl_simple_arc_ffi!(
- LayerBlockRule,
- Servo_LayerBlockRule_AddRef,
- Servo_LayerBlockRule_Release
-);
-impl_simple_arc_ffi!(
- LayerStatementRule,
- Servo_LayerStatementRule_AddRef,
- Servo_LayerStatementRule_Release
-);
-impl_locked_arc_ffi!(
- MediaList,
- LockedMediaList,
- Servo_MediaList_AddRef,
- Servo_MediaList_Release
-);
-impl_simple_arc_ffi!(MediaRule, Servo_MediaRule_AddRef, Servo_MediaRule_Release);
-impl_simple_arc_ffi!(
- NamespaceRule,
- Servo_NamespaceRule_AddRef,
- Servo_NamespaceRule_Release
-);
-impl_locked_arc_ffi!(
- PageRule,
- LockedPageRule,
- Servo_PageRule_AddRef,
- Servo_PageRule_Release
-);
-impl_simple_arc_ffi!(
- PropertyRule,
- Servo_PropertyRule_AddRef,
- Servo_PropertyRule_Release
-);
-impl_simple_arc_ffi!(
- SupportsRule,
- Servo_SupportsRule_AddRef,
- Servo_SupportsRule_Release
-);
-impl_simple_arc_ffi!(
- ContainerRule,
- Servo_ContainerRule_AddRef,
- Servo_ContainerRule_Release
-);
-impl_simple_arc_ffi!(
- DocumentRule,
- Servo_DocumentRule_AddRef,
- Servo_DocumentRule_Release
-);
-impl_simple_arc_ffi!(
- FontFeatureValuesRule,
- Servo_FontFeatureValuesRule_AddRef,
- Servo_FontFeatureValuesRule_Release
-);
-impl_simple_arc_ffi!(
- FontPaletteValuesRule,
- Servo_FontPaletteValuesRule_AddRef,
- Servo_FontPaletteValuesRule_Release
-);
-impl_locked_arc_ffi!(
- FontFaceRule,
- LockedFontFaceRule,
- Servo_FontFaceRule_AddRef,
- Servo_FontFaceRule_Release
-);
-impl_locked_arc_ffi!(
- CounterStyleRule,
- LockedCounterStyleRule,
- Servo_CounterStyleRule_AddRef,
- Servo_CounterStyleRule_Release
-);
-
-impl_simple_arc_ffi!(
- StylesheetContents,
- Servo_StyleSheetContents_AddRef,
- Servo_StyleSheetContents_Release
-);
-impl_simple_arc_ffi!(
- CssUrlData,
- Servo_CssUrlData_AddRef,
- Servo_CssUrlData_Release
-);
-impl_simple_arc_ffi!(
- ComputedValues,
- Servo_ComputedStyle_AddRef,
- Servo_ComputedStyle_Release
-);
-impl_simple_arc_ffi!(
- AnimationValue,
- Servo_AnimationValue_AddRef,
- Servo_AnimationValue_Release
-);
diff --git a/components/style/gecko/conversions.rs b/components/style/gecko/conversions.rs
deleted file mode 100644
index ea3700a3235..00000000000
--- a/components/style/gecko/conversions.rs
+++ /dev/null
@@ -1,59 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
-
-//! This module contains conversion helpers between Servo and Gecko types
-//! Ideally, it would be in geckolib itself, but coherence
-//! forces us to keep the traits and implementations here
-//!
-//! FIXME(emilio): This file should generally just die.
-
-#![allow(unsafe_code)]
-
-use crate::gecko_bindings::structs::{nsresult, Matrix4x4Components};
-use crate::stylesheets::RulesMutateError;
-use crate::values::computed::transform::Matrix3D;
-
-impl From<RulesMutateError> for nsresult {
- fn from(other: RulesMutateError) -> Self {
- match other {
- RulesMutateError::Syntax => nsresult::NS_ERROR_DOM_SYNTAX_ERR,
- RulesMutateError::IndexSize => nsresult::NS_ERROR_DOM_INDEX_SIZE_ERR,
- RulesMutateError::HierarchyRequest => nsresult::NS_ERROR_DOM_HIERARCHY_REQUEST_ERR,
- RulesMutateError::InvalidState => nsresult::NS_ERROR_DOM_INVALID_STATE_ERR,
- }
- }
-}
-
-impl<'a> From<&'a Matrix4x4Components> for Matrix3D {
- fn from(m: &'a Matrix4x4Components) -> Matrix3D {
- Matrix3D {
- m11: m[0],
- m12: m[1],
- m13: m[2],
- m14: m[3],
- m21: m[4],
- m22: m[5],
- m23: m[6],
- m24: m[7],
- m31: m[8],
- m32: m[9],
- m33: m[10],
- m34: m[11],
- m41: m[12],
- m42: m[13],
- m43: m[14],
- m44: m[15],
- }
- }
-}
-
-impl From<Matrix3D> for Matrix4x4Components {
- fn from(matrix: Matrix3D) -> Self {
- [
- matrix.m11, matrix.m12, matrix.m13, matrix.m14, matrix.m21, matrix.m22, matrix.m23,
- matrix.m24, matrix.m31, matrix.m32, matrix.m33, matrix.m34, matrix.m41, matrix.m42,
- matrix.m43, matrix.m44,
- ]
- }
-}
diff --git a/components/style/gecko/data.rs b/components/style/gecko/data.rs
deleted file mode 100644
index c4a5554c5e5..00000000000
--- a/components/style/gecko/data.rs
+++ /dev/null
@@ -1,198 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
-
-//! Data needed to style a Gecko document.
-
-use crate::dom::TElement;
-use crate::gecko_bindings::bindings;
-use crate::gecko_bindings::structs::{
- self, ServoStyleSetSizes, StyleSheet as DomStyleSheet, StyleSheetInfo,
-};
-use crate::invalidation::media_queries::{MediaListKey, ToMediaListKey};
-use crate::media_queries::{Device, MediaList};
-use crate::properties::ComputedValues;
-use crate::selector_parser::SnapshotMap;
-use crate::shared_lock::{SharedRwLockReadGuard, StylesheetGuards};
-use crate::stylesheets::{StylesheetContents, StylesheetInDocument};
-use crate::stylist::Stylist;
-use atomic_refcell::{AtomicRef, AtomicRefCell, AtomicRefMut};
-use malloc_size_of::MallocSizeOfOps;
-use servo_arc::Arc;
-use std::fmt;
-
-/// Little wrapper to a Gecko style sheet.
-#[derive(Eq, PartialEq)]
-pub struct GeckoStyleSheet(*const DomStyleSheet);
-
-// NOTE(emilio): These are kind of a lie. We allow to make these Send + Sync so that other data
-// structures can also be Send and Sync, but Gecko's stylesheets are main-thread-reference-counted.
-//
-// We assert that we reference-count in the right thread (in the Addref/Release implementations).
-// Sending these to a different thread can't really happen (it could theoretically really happen if
-// we allowed @import rules inside a nested style rule, but that can't happen per spec and would be
-// a parser bug, caught by the asserts).
-unsafe impl Send for GeckoStyleSheet {}
-unsafe impl Sync for GeckoStyleSheet {}
-
-impl fmt::Debug for GeckoStyleSheet {
- fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
- let contents = self.contents();
- formatter
- .debug_struct("GeckoStyleSheet")
- .field("origin", &contents.origin)
- .field("url_data", &*contents.url_data.read())
- .finish()
- }
-}
-
-impl ToMediaListKey for crate::gecko::data::GeckoStyleSheet {
- fn to_media_list_key(&self) -> MediaListKey {
- use std::mem;
- unsafe { MediaListKey::from_raw(mem::transmute(self.0)) }
- }
-}
-
-impl GeckoStyleSheet {
- /// Create a `GeckoStyleSheet` from a raw `DomStyleSheet` pointer.
- #[inline]
- pub unsafe fn new(s: *const DomStyleSheet) -> Self {
- debug_assert!(!s.is_null());
- bindings::Gecko_StyleSheet_AddRef(s);
- Self::from_addrefed(s)
- }
-
- /// Create a `GeckoStyleSheet` from a raw `DomStyleSheet` pointer that
- /// already holds a strong reference.
- #[inline]
- pub unsafe fn from_addrefed(s: *const DomStyleSheet) -> Self {
- assert!(!s.is_null());
- GeckoStyleSheet(s)
- }
-
- /// HACK(emilio): This is so that we can avoid crashing release due to
- /// bug 1719963 and can hopefully get a useful report from fuzzers.
- #[inline]
- pub fn hack_is_null(&self) -> bool {
- self.0.is_null()
- }
-
- /// Get the raw `StyleSheet` that we're wrapping.
- pub fn raw(&self) -> &DomStyleSheet {
- unsafe { &*self.0 }
- }
-
- fn inner(&self) -> &StyleSheetInfo {
- unsafe { &*(self.raw().mInner as *const StyleSheetInfo) }
- }
-}
-
-impl Drop for GeckoStyleSheet {
- fn drop(&mut self) {
- unsafe { bindings::Gecko_StyleSheet_Release(self.0) };
- }
-}
-
-impl Clone for GeckoStyleSheet {
- fn clone(&self) -> Self {
- unsafe { bindings::Gecko_StyleSheet_AddRef(self.0) };
- GeckoStyleSheet(self.0)
- }
-}
-
-impl StylesheetInDocument for GeckoStyleSheet {
- fn media<'a>(&'a self, guard: &'a SharedRwLockReadGuard) -> Option<&'a MediaList> {
- use crate::gecko_bindings::structs::mozilla::dom::MediaList as DomMediaList;
- unsafe {
- let dom_media_list = self.raw().mMedia.mRawPtr as *const DomMediaList;
- if dom_media_list.is_null() {
- return None;
- }
- let list = &*(*dom_media_list).mRawList.mRawPtr;
- Some(list.read_with(guard))
- }
- }
-
- // All the stylesheets Servo knows about are enabled, because that state is
- // handled externally by Gecko.
- #[inline]
- fn enabled(&self) -> bool {
- true
- }
-
- #[inline]
- fn contents(&self) -> &StylesheetContents {
- debug_assert!(!self.inner().mContents.mRawPtr.is_null());
- unsafe { &*self.inner().mContents.mRawPtr }
- }
-}
-
-/// The container for data that a Servo-backed Gecko document needs to style
-/// itself.
-pub struct PerDocumentStyleDataImpl {
- /// Rule processor.
- pub stylist: Stylist,
-
- /// A cache from element to resolved style.
- pub undisplayed_style_cache: crate::traversal::UndisplayedStyleCache,
-
- /// The generation for which our cache is valid.
- pub undisplayed_style_cache_generation: u64,
-}
-
-/// The data itself is an `AtomicRefCell`, which guarantees the proper semantics
-/// and unexpected races while trying to mutate it.
-pub struct PerDocumentStyleData(AtomicRefCell<PerDocumentStyleDataImpl>);
-
-impl PerDocumentStyleData {
- /// Create a `PerDocumentStyleData`.
- pub fn new(document: *const structs::Document) -> Self {
- let device = Device::new(document);
- let quirks_mode = device.document().mCompatMode;
-
- PerDocumentStyleData(AtomicRefCell::new(PerDocumentStyleDataImpl {
- stylist: Stylist::new(device, quirks_mode.into()),
- undisplayed_style_cache: Default::default(),
- undisplayed_style_cache_generation: 0,
- }))
- }
-
- /// Get an immutable reference to this style data.
- pub fn borrow(&self) -> AtomicRef<PerDocumentStyleDataImpl> {
- self.0.borrow()
- }
-
- /// Get an mutable reference to this style data.
- pub fn borrow_mut(&self) -> AtomicRefMut<PerDocumentStyleDataImpl> {
- self.0.borrow_mut()
- }
-}
-
-impl PerDocumentStyleDataImpl {
- /// Recreate the style data if the stylesheets have changed.
- pub fn flush_stylesheets<E>(
- &mut self,
- guard: &SharedRwLockReadGuard,
- document_element: Option<E>,
- snapshots: Option<&SnapshotMap>,
- ) -> bool
- where
- E: TElement,
- {
- self.stylist
- .flush(&StylesheetGuards::same(guard), document_element, snapshots)
- }
-
- /// Get the default computed values for this document.
- pub fn default_computed_values(&self) -> &Arc<ComputedValues> {
- self.stylist.device().default_computed_values_arc()
- }
-
- /// Measure heap usage.
- pub fn add_size_of(&self, ops: &mut MallocSizeOfOps, sizes: &mut ServoStyleSetSizes) {
- self.stylist.add_size_of(ops, sizes);
- }
-}
-
-/// The gecko-specific AuthorStyles instantiation.
-pub type AuthorStyles = crate::author_styles::AuthorStyles<GeckoStyleSheet>;
diff --git a/components/style/gecko/media_features.rs b/components/style/gecko/media_features.rs
deleted file mode 100644
index 4ca746ea84d..00000000000
--- a/components/style/gecko/media_features.rs
+++ /dev/null
@@ -1,1028 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
-
-//! Gecko's media feature list and evaluator.
-
-use crate::gecko_bindings::bindings;
-use crate::gecko_bindings::structs;
-use crate::gecko_bindings::structs::ScreenColorGamut;
-use crate::media_queries::{Device, MediaType};
-use crate::queries::feature::{AllowsRanges, Evaluator, FeatureFlags, QueryFeatureDescription};
-use crate::queries::values::Orientation;
-use crate::values::computed::{CSSPixelLength, Context, Ratio, Resolution};
-use app_units::Au;
-use euclid::default::Size2D;
-
-fn device_size(device: &Device) -> Size2D<Au> {
- let mut width = 0;
- let mut height = 0;
- unsafe {
- bindings::Gecko_MediaFeatures_GetDeviceSize(device.document(), &mut width, &mut height);
- }
- Size2D::new(Au(width), Au(height))
-}
-
-/// https://drafts.csswg.org/mediaqueries-4/#width
-fn eval_width(context: &Context) -> CSSPixelLength {
- CSSPixelLength::new(context.device().au_viewport_size().width.to_f32_px())
-}
-
-/// https://drafts.csswg.org/mediaqueries-4/#device-width
-fn eval_device_width(context: &Context) -> CSSPixelLength {
- CSSPixelLength::new(device_size(context.device()).width.to_f32_px())
-}
-
-/// https://drafts.csswg.org/mediaqueries-4/#height
-fn eval_height(context: &Context) -> CSSPixelLength {
- CSSPixelLength::new(context.device().au_viewport_size().height.to_f32_px())
-}
-
-/// https://drafts.csswg.org/mediaqueries-4/#device-height
-fn eval_device_height(context: &Context) -> CSSPixelLength {
- CSSPixelLength::new(device_size(context.device()).height.to_f32_px())
-}
-
-fn eval_aspect_ratio_for<F>(context: &Context, get_size: F) -> Ratio
-where
- F: FnOnce(&Device) -> Size2D<Au>,
-{
- let size = get_size(context.device());
- Ratio::new(size.width.0 as f32, size.height.0 as f32)
-}
-
-/// https://drafts.csswg.org/mediaqueries-4/#aspect-ratio
-fn eval_aspect_ratio(context: &Context) -> Ratio {
- eval_aspect_ratio_for(context, Device::au_viewport_size)
-}
-
-/// https://drafts.csswg.org/mediaqueries-4/#device-aspect-ratio
-fn eval_device_aspect_ratio(context: &Context) -> Ratio {
- eval_aspect_ratio_for(context, device_size)
-}
-
-/// https://compat.spec.whatwg.org/#css-media-queries-webkit-device-pixel-ratio
-fn eval_device_pixel_ratio(context: &Context) -> f32 {
- eval_resolution(context).dppx()
-}
-
-/// https://drafts.csswg.org/mediaqueries-4/#orientation
-fn eval_orientation(context: &Context, value: Option<Orientation>) -> bool {
- Orientation::eval(context.device().au_viewport_size(), value)
-}
-
-/// FIXME: There's no spec for `-moz-device-orientation`.
-fn eval_device_orientation(context: &Context, value: Option<Orientation>) -> bool {
- Orientation::eval(device_size(context.device()), value)
-}
-
-/// Values for the display-mode media feature.
-#[derive(Clone, Copy, Debug, FromPrimitive, Parse, PartialEq, ToCss)]
-#[repr(u8)]
-#[allow(missing_docs)]
-pub enum DisplayMode {
- Browser = 0,
- MinimalUi,
- Standalone,
- Fullscreen,
-}
-
-/// https://w3c.github.io/manifest/#the-display-mode-media-feature
-fn eval_display_mode(context: &Context, query_value: Option<DisplayMode>) -> bool {
- match query_value {
- Some(v) => {
- v == unsafe {
- bindings::Gecko_MediaFeatures_GetDisplayMode(context.device().document())
- }
- },
- None => true,
- }
-}
-
-/// https://drafts.csswg.org/mediaqueries-4/#grid
-fn eval_grid(_: &Context) -> bool {
- // Gecko doesn't support grid devices (e.g., ttys), so the 'grid' feature
- // is always 0.
- false
-}
-
-/// https://compat.spec.whatwg.org/#css-media-queries-webkit-transform-3d
-fn eval_transform_3d(_: &Context) -> bool {
- true
-}
-
-#[derive(Clone, Copy, Debug, FromPrimitive, Parse, ToCss)]
-#[repr(u8)]
-enum Scan {
- Progressive,
- Interlace,
-}
-
-/// https://drafts.csswg.org/mediaqueries-4/#scan
-fn eval_scan(_: &Context, _: Option<Scan>) -> bool {
- // Since Gecko doesn't support the 'tv' media type, the 'scan' feature never
- // matches.
- false
-}
-
-/// https://drafts.csswg.org/mediaqueries-4/#color
-fn eval_color(context: &Context) -> i32 {
- unsafe { bindings::Gecko_MediaFeatures_GetColorDepth(context.device().document()) }
-}
-
-/// https://drafts.csswg.org/mediaqueries-4/#color-index
-fn eval_color_index(_: &Context) -> i32 {
- // We should return zero if the device does not use a color lookup table.
- 0
-}
-
-/// https://drafts.csswg.org/mediaqueries-4/#monochrome
-fn eval_monochrome(context: &Context) -> i32 {
- // For color devices we should return 0.
- unsafe { bindings::Gecko_MediaFeatures_GetMonochromeBitsPerPixel(context.device().document()) }
-}
-
-/// Values for the color-gamut media feature.
-/// This implements PartialOrd so that lower values will correctly match
-/// higher capabilities.
-#[derive(Clone, Copy, Debug, FromPrimitive, Parse, PartialEq, PartialOrd, ToCss)]
-#[repr(u8)]
-enum ColorGamut {
- /// The sRGB gamut.
- Srgb,
- /// The gamut specified by the Display P3 Color Space.
- P3,
- /// The gamut specified by the ITU-R Recommendation BT.2020 Color Space.
- Rec2020,
-}
-
-/// https://drafts.csswg.org/mediaqueries-4/#color-gamut
-fn eval_color_gamut(context: &Context, query_value: Option<ColorGamut>) -> bool {
- let query_value = match query_value {
- Some(v) => v,
- None => return false,
- };
- let color_gamut =
- unsafe { bindings::Gecko_MediaFeatures_ColorGamut(context.device().document()) };
- // Match if our color gamut is at least as wide as the query value
- query_value <=
- match color_gamut {
- // EndGuard_ is not a valid color gamut, so the default color-gamut is used.
- ScreenColorGamut::Srgb | ScreenColorGamut::EndGuard_ => ColorGamut::Srgb,
- ScreenColorGamut::P3 => ColorGamut::P3,
- ScreenColorGamut::Rec2020 => ColorGamut::Rec2020,
- }
-}
-
-/// https://drafts.csswg.org/mediaqueries-4/#resolution
-fn eval_resolution(context: &Context) -> Resolution {
- let resolution_dppx =
- unsafe { bindings::Gecko_MediaFeatures_GetResolution(context.device().document()) };
- Resolution::from_dppx(resolution_dppx)
-}
-
-#[derive(Clone, Copy, Debug, FromPrimitive, Parse, ToCss)]
-#[repr(u8)]
-enum PrefersReducedMotion {
- NoPreference,
- Reduce,
-}
-
-#[derive(Clone, Copy, Debug, FromPrimitive, Parse, ToCss)]
-#[repr(u8)]
-enum PrefersReducedTransparency {
- NoPreference,
- Reduce,
-}
-
-/// Values for the prefers-color-scheme media feature.
-#[derive(Clone, Copy, Debug, FromPrimitive, Parse, PartialEq, ToCss)]
-#[repr(u8)]
-#[allow(missing_docs)]
-pub enum PrefersColorScheme {
- Light,
- Dark,
-}
-
-/// Values for the dynamic-range and video-dynamic-range media features.
-/// https://drafts.csswg.org/mediaqueries-5/#dynamic-range
-/// This implements PartialOrd so that lower values will correctly match
-/// higher capabilities.
-#[derive(Clone, Copy, Debug, FromPrimitive, Parse, PartialEq, PartialOrd, ToCss)]
-#[repr(u8)]
-#[allow(missing_docs)]
-pub enum DynamicRange {
- Standard,
- High,
-}
-
-/// https://drafts.csswg.org/mediaqueries-5/#prefers-reduced-motion
-fn eval_prefers_reduced_motion(
- context: &Context,
- query_value: Option<PrefersReducedMotion>,
-) -> bool {
- let prefers_reduced =
- unsafe { bindings::Gecko_MediaFeatures_PrefersReducedMotion(context.device().document()) };
- let query_value = match query_value {
- Some(v) => v,
- None => return prefers_reduced,
- };
-
- match query_value {
- PrefersReducedMotion::NoPreference => !prefers_reduced,
- PrefersReducedMotion::Reduce => prefers_reduced,
- }
-}
-
-/// https://drafts.csswg.org/mediaqueries-5/#prefers-reduced-transparency
-fn eval_prefers_reduced_transparency(
- context: &Context,
- query_value: Option<PrefersReducedTransparency>,
-) -> bool {
- let prefers_reduced = unsafe {
- bindings::Gecko_MediaFeatures_PrefersReducedTransparency(context.device().document())
- };
- let query_value = match query_value {
- Some(v) => v,
- None => return prefers_reduced,
- };
-
- match query_value {
- PrefersReducedTransparency::NoPreference => !prefers_reduced,
- PrefersReducedTransparency::Reduce => prefers_reduced,
- }
-}
-
-/// Possible values for prefers-contrast media query.
-/// https://drafts.csswg.org/mediaqueries-5/#prefers-contrast
-#[derive(Clone, Copy, Debug, FromPrimitive, Parse, PartialEq, ToCss)]
-#[repr(u8)]
-pub enum PrefersContrast {
- /// More contrast is preferred.
- More,
- /// Low contrast is preferred.
- Less,
- /// Custom (not more, not less).
- Custom,
- /// The default value if neither high or low contrast is enabled.
- NoPreference,
-}
-
-/// https://drafts.csswg.org/mediaqueries-5/#prefers-contrast
-fn eval_prefers_contrast(context: &Context, query_value: Option<PrefersContrast>) -> bool {
- let prefers_contrast =
- unsafe { bindings::Gecko_MediaFeatures_PrefersContrast(context.device().document()) };
- match query_value {
- Some(v) => v == prefers_contrast,
- None => prefers_contrast != PrefersContrast::NoPreference,
- }
-}
-
-/// Possible values for the forced-colors media query.
-/// https://drafts.csswg.org/mediaqueries-5/#forced-colors
-#[derive(Clone, Copy, Debug, FromPrimitive, Parse, PartialEq, ToCss)]
-#[repr(u8)]
-pub enum ForcedColors {
- /// Page colors are not being forced.
- None,
- /// Page colors are being forced.
- Active,
-}
-
-/// https://drafts.csswg.org/mediaqueries-5/#forced-colors
-fn eval_forced_colors(context: &Context, query_value: Option<ForcedColors>) -> bool {
- let forced = !context.device().use_document_colors();
- match query_value {
- Some(query_value) => forced == (query_value == ForcedColors::Active),
- None => forced,
- }
-}
-
-/// Possible values for the inverted-colors media query.
-/// https://drafts.csswg.org/mediaqueries-5/#inverted
-#[derive(Clone, Copy, Debug, FromPrimitive, Parse, ToCss)]
-#[repr(u8)]
-enum InvertedColors {
- /// Colors are displayed normally.
- None,
- /// All pixels within the displayed area have been inverted.
- Inverted,
-}
-
-/// https://drafts.csswg.org/mediaqueries-5/#inverted
-fn eval_inverted_colors(context: &Context, query_value: Option<InvertedColors>) -> bool {
- let inverted_colors =
- unsafe { bindings::Gecko_MediaFeatures_InvertedColors(context.device().document()) };
- let query_value = match query_value {
- Some(v) => v,
- None => return inverted_colors,
- };
-
- match query_value {
- InvertedColors::None => !inverted_colors,
- InvertedColors::Inverted => inverted_colors,
- }
-}
-
-#[derive(Clone, Copy, Debug, FromPrimitive, Parse, ToCss)]
-#[repr(u8)]
-enum OverflowBlock {
- None,
- Scroll,
- Paged,
-}
-
-/// https://drafts.csswg.org/mediaqueries-4/#mf-overflow-block
-fn eval_overflow_block(context: &Context, query_value: Option<OverflowBlock>) -> bool {
- // For the time being, assume that printing (including previews)
- // is the only time when we paginate, and we are otherwise always
- // scrolling. This is true at the moment in Firefox, but may need
- // updating in the future (e.g., ebook readers built with Stylo, a
- // billboard mode that doesn't support overflow at all).
- //
- // If this ever changes, don't forget to change eval_overflow_inline too.
- let scrolling = context.device().media_type() != MediaType::print();
- let query_value = match query_value {
- Some(v) => v,
- None => return true,
- };
-
- match query_value {
- OverflowBlock::None => false,
- OverflowBlock::Scroll => scrolling,
- OverflowBlock::Paged => !scrolling,
- }
-}
-
-#[derive(Clone, Copy, Debug, FromPrimitive, Parse, ToCss)]
-#[repr(u8)]
-enum OverflowInline {
- None,
- Scroll,
-}
-
-/// https://drafts.csswg.org/mediaqueries-4/#mf-overflow-inline
-fn eval_overflow_inline(context: &Context, query_value: Option<OverflowInline>) -> bool {
- // See the note in eval_overflow_block.
- let scrolling = context.device().media_type() != MediaType::print();
- let query_value = match query_value {
- Some(v) => v,
- None => return scrolling,
- };
-
- match query_value {
- OverflowInline::None => !scrolling,
- OverflowInline::Scroll => scrolling,
- }
-}
-
-#[derive(Clone, Copy, Debug, FromPrimitive, Parse, ToCss)]
-#[repr(u8)]
-enum Update {
- None,
- Slow,
- Fast,
-}
-
-/// https://drafts.csswg.org/mediaqueries-4/#update
-fn eval_update(context: &Context, query_value: Option<Update>) -> bool {
- // This has similar caveats to those described in eval_overflow_block.
- // For now, we report that print (incl. print media simulation,
- // which can in fact update but is limited to the developer tools)
- // is `update: none` and that all other contexts are `update: fast`,
- // which may not be true for future platforms, like e-ink devices.
- let can_update = context.device().media_type() != MediaType::print();
- let query_value = match query_value {
- Some(v) => v,
- None => return can_update,
- };
-
- match query_value {
- Update::None => !can_update,
- Update::Slow => false,
- Update::Fast => can_update,
- }
-}
-
-fn do_eval_prefers_color_scheme(
- context: &Context,
- use_content: bool,
- query_value: Option<PrefersColorScheme>,
-) -> bool {
- let prefers_color_scheme = unsafe {
- bindings::Gecko_MediaFeatures_PrefersColorScheme(context.device().document(), use_content)
- };
- match query_value {
- Some(v) => prefers_color_scheme == v,
- None => true,
- }
-}
-
-/// https://drafts.csswg.org/mediaqueries-5/#prefers-color-scheme
-fn eval_prefers_color_scheme(context: &Context, query_value: Option<PrefersColorScheme>) -> bool {
- do_eval_prefers_color_scheme(context, /* use_content = */ false, query_value)
-}
-
-fn eval_content_prefers_color_scheme(
- context: &Context,
- query_value: Option<PrefersColorScheme>,
-) -> bool {
- do_eval_prefers_color_scheme(context, /* use_content = */ true, query_value)
-}
-
-/// https://drafts.csswg.org/mediaqueries-5/#dynamic-range
-fn eval_dynamic_range(context: &Context, query_value: Option<DynamicRange>) -> bool {
- let dynamic_range =
- unsafe { bindings::Gecko_MediaFeatures_DynamicRange(context.device().document()) };
- match query_value {
- Some(v) => dynamic_range >= v,
- None => false,
- }
-}
-/// https://drafts.csswg.org/mediaqueries-5/#video-dynamic-range
-fn eval_video_dynamic_range(context: &Context, query_value: Option<DynamicRange>) -> bool {
- let dynamic_range =
- unsafe { bindings::Gecko_MediaFeatures_VideoDynamicRange(context.device().document()) };
- match query_value {
- Some(v) => dynamic_range >= v,
- None => false,
- }
-}
-
-bitflags! {
- /// https://drafts.csswg.org/mediaqueries-4/#mf-interaction
- struct PointerCapabilities: u8 {
- const COARSE = structs::PointerCapabilities_Coarse;
- const FINE = structs::PointerCapabilities_Fine;
- const HOVER = structs::PointerCapabilities_Hover;
- }
-}
-
-fn primary_pointer_capabilities(context: &Context) -> PointerCapabilities {
- PointerCapabilities::from_bits_truncate(unsafe {
- bindings::Gecko_MediaFeatures_PrimaryPointerCapabilities(context.device().document())
- })
-}
-
-fn all_pointer_capabilities(context: &Context) -> PointerCapabilities {
- PointerCapabilities::from_bits_truncate(unsafe {
- bindings::Gecko_MediaFeatures_AllPointerCapabilities(context.device().document())
- })
-}
-
-#[derive(Clone, Copy, Debug, FromPrimitive, Parse, ToCss)]
-#[repr(u8)]
-enum Pointer {
- None,
- Coarse,
- Fine,
-}
-
-fn eval_pointer_capabilities(
- query_value: Option<Pointer>,
- pointer_capabilities: PointerCapabilities,
-) -> bool {
- let query_value = match query_value {
- Some(v) => v,
- None => return !pointer_capabilities.is_empty(),
- };
-
- match query_value {
- Pointer::None => pointer_capabilities.is_empty(),
- Pointer::Coarse => pointer_capabilities.intersects(PointerCapabilities::COARSE),
- Pointer::Fine => pointer_capabilities.intersects(PointerCapabilities::FINE),
- }
-}
-
-/// https://drafts.csswg.org/mediaqueries-4/#pointer
-fn eval_pointer(context: &Context, query_value: Option<Pointer>) -> bool {
- eval_pointer_capabilities(query_value, primary_pointer_capabilities(context))
-}
-
-/// https://drafts.csswg.org/mediaqueries-4/#descdef-media-any-pointer
-fn eval_any_pointer(context: &Context, query_value: Option<Pointer>) -> bool {
- eval_pointer_capabilities(query_value, all_pointer_capabilities(context))
-}
-
-#[derive(Clone, Copy, Debug, FromPrimitive, Parse, ToCss)]
-#[repr(u8)]
-enum Hover {
- None,
- Hover,
-}
-
-fn eval_hover_capabilities(
- query_value: Option<Hover>,
- pointer_capabilities: PointerCapabilities,
-) -> bool {
- let can_hover = pointer_capabilities.intersects(PointerCapabilities::HOVER);
- let query_value = match query_value {
- Some(v) => v,
- None => return can_hover,
- };
-
- match query_value {
- Hover::None => !can_hover,
- Hover::Hover => can_hover,
- }
-}
-
-/// https://drafts.csswg.org/mediaqueries-4/#hover
-fn eval_hover(context: &Context, query_value: Option<Hover>) -> bool {
- eval_hover_capabilities(query_value, primary_pointer_capabilities(context))
-}
-
-/// https://drafts.csswg.org/mediaqueries-4/#descdef-media-any-hover
-fn eval_any_hover(context: &Context, query_value: Option<Hover>) -> bool {
- eval_hover_capabilities(query_value, all_pointer_capabilities(context))
-}
-
-fn eval_moz_is_glyph(context: &Context) -> bool {
- context.device().document().mIsSVGGlyphsDocument()
-}
-
-fn eval_moz_print_preview(context: &Context) -> bool {
- let is_print_preview = context.device().is_print_preview();
- if is_print_preview {
- debug_assert_eq!(context.device().media_type(), MediaType::print());
- }
- is_print_preview
-}
-
-fn eval_moz_non_native_content_theme(context: &Context) -> bool {
- unsafe { bindings::Gecko_MediaFeatures_ShouldAvoidNativeTheme(context.device().document()) }
-}
-
-fn eval_moz_is_resource_document(context: &Context) -> bool {
- unsafe { bindings::Gecko_MediaFeatures_IsResourceDocument(context.device().document()) }
-}
-
-/// Allows front-end CSS to discern platform via media queries.
-#[derive(Clone, Copy, Debug, FromPrimitive, Parse, ToCss)]
-#[repr(u8)]
-pub enum Platform {
- /// Matches any Android version.
- Android,
- /// For our purposes here, "linux" is just "gtk" (so unix-but-not-mac).
- /// There's no need for our front-end code to differentiate between those
- /// platforms and they already use the "linux" string elsewhere (e.g.,
- /// toolkit/themes/linux).
- Linux,
- /// Matches any macOS version.
- Macos,
- /// Matches any Windows version.
- Windows,
- /// Matches only Windows 7.
- WindowsWin7,
- /// Matches only Windows 8.
- WindowsWin8,
- /// Matches windows 10 and actually matches windows 11 too, as of right now.
- WindowsWin10,
-}
-
-fn eval_moz_platform(_: &Context, query_value: Option<Platform>) -> bool {
- let query_value = match query_value {
- Some(v) => v,
- None => return false,
- };
-
- unsafe { bindings::Gecko_MediaFeatures_MatchesPlatform(query_value) }
-}
-
-/// Values for the scripting media feature.
-/// https://drafts.csswg.org/mediaqueries-5/#scripting
-#[derive(Clone, Copy, Debug, FromPrimitive, Parse, PartialEq, ToCss)]
-#[repr(u8)]
-pub enum Scripting {
- /// Scripting is not supported or not enabled
- None,
- /// Scripting is supported and enabled, but only for initial page load
- /// We will never match this value as it is intended for non-browser user agents,
- /// but it is part of the spec so we should still parse it.
- /// See: https://github.com/w3c/csswg-drafts/issues/8621
- InitialOnly,
- /// Scripting is supported and enabled
- Enabled,
-}
-
-/// https://drafts.csswg.org/mediaqueries-5/#scripting
-fn eval_scripting(context: &Context, query_value: Option<Scripting>) -> bool {
- let scripting = unsafe { bindings::Gecko_MediaFeatures_Scripting(context.device().document()) };
- match query_value {
- Some(v) => v == scripting,
- None => scripting != Scripting::None,
- }
-}
-
-fn eval_moz_windows_non_native_menus(context: &Context) -> bool {
- unsafe { bindings::Gecko_MediaFeatures_WindowsNonNativeMenus(context.device().document()) }
-}
-
-fn eval_moz_overlay_scrollbars(context: &Context) -> bool {
- unsafe { bindings::Gecko_MediaFeatures_UseOverlayScrollbars(context.device().document()) }
-}
-
-fn get_lnf_int(int_id: i32) -> i32 {
- unsafe { bindings::Gecko_GetLookAndFeelInt(int_id) }
-}
-
-fn get_lnf_int_as_bool(int_id: i32) -> bool {
- get_lnf_int(int_id) != 0
-}
-
-fn get_scrollbar_start_backward(int_id: i32) -> bool {
- (get_lnf_int(int_id) & bindings::LookAndFeel_eScrollArrow_StartBackward as i32) != 0
-}
-
-fn get_scrollbar_start_forward(int_id: i32) -> bool {
- (get_lnf_int(int_id) & bindings::LookAndFeel_eScrollArrow_StartForward as i32) != 0
-}
-
-fn get_scrollbar_end_backward(int_id: i32) -> bool {
- (get_lnf_int(int_id) & bindings::LookAndFeel_eScrollArrow_EndBackward as i32) != 0
-}
-
-fn get_scrollbar_end_forward(int_id: i32) -> bool {
- (get_lnf_int(int_id) & bindings::LookAndFeel_eScrollArrow_EndForward as i32) != 0
-}
-
-macro_rules! lnf_int_feature {
- ($feature_name:expr, $int_id:ident, $get_value:ident) => {{
- fn __eval(_: &Context) -> bool {
- $get_value(bindings::LookAndFeel_IntID::$int_id as i32)
- }
-
- feature!(
- $feature_name,
- AllowsRanges::No,
- Evaluator::BoolInteger(__eval),
- FeatureFlags::CHROME_AND_UA_ONLY,
- )
- }};
- ($feature_name:expr, $int_id:ident) => {{
- lnf_int_feature!($feature_name, $int_id, get_lnf_int_as_bool)
- }};
-}
-
-/// bool pref-based features are an slightly less convenient to start using
-/// version of @supports -moz-bool-pref, but with some benefits, mainly that
-/// they can support dynamic changes, and don't require a pref lookup every time
-/// they're used.
-///
-/// In order to use them you need to make sure that the pref defined as a static
-/// pref, with `rust: true`. The feature name needs to be defined in
-/// `StaticAtoms.py` just like the others. In order to support dynamic changes,
-/// you also need to add them to kMediaQueryPrefs in nsXPLookAndFeel.cpp
-#[allow(unused)]
-macro_rules! bool_pref_feature {
- ($feature_name:expr, $pref:tt) => {{
- fn __eval(_: &Context) -> bool {
- static_prefs::pref!($pref)
- }
-
- feature!(
- $feature_name,
- AllowsRanges::No,
- Evaluator::BoolInteger(__eval),
- FeatureFlags::CHROME_AND_UA_ONLY,
- )
- }};
-}
-
-/// Adding new media features requires (1) adding the new feature to this
-/// array, with appropriate entries (and potentially any new code needed
-/// to support new types in these entries and (2) ensuring that either
-/// nsPresContext::MediaFeatureValuesChanged is called when the value that
-/// would be returned by the evaluator function could change.
-pub static MEDIA_FEATURES: [QueryFeatureDescription; 67] = [
- feature!(
- atom!("width"),
- AllowsRanges::Yes,
- Evaluator::Length(eval_width),
- FeatureFlags::VIEWPORT_DEPENDENT,
- ),
- feature!(
- atom!("height"),
- AllowsRanges::Yes,
- Evaluator::Length(eval_height),
- FeatureFlags::VIEWPORT_DEPENDENT,
- ),
- feature!(
- atom!("aspect-ratio"),
- AllowsRanges::Yes,
- Evaluator::NumberRatio(eval_aspect_ratio),
- FeatureFlags::VIEWPORT_DEPENDENT,
- ),
- feature!(
- atom!("orientation"),
- AllowsRanges::No,
- keyword_evaluator!(eval_orientation, Orientation),
- FeatureFlags::VIEWPORT_DEPENDENT,
- ),
- feature!(
- atom!("device-width"),
- AllowsRanges::Yes,
- Evaluator::Length(eval_device_width),
- FeatureFlags::empty(),
- ),
- feature!(
- atom!("device-height"),
- AllowsRanges::Yes,
- Evaluator::Length(eval_device_height),
- FeatureFlags::empty(),
- ),
- feature!(
- atom!("device-aspect-ratio"),
- AllowsRanges::Yes,
- Evaluator::NumberRatio(eval_device_aspect_ratio),
- FeatureFlags::empty(),
- ),
- feature!(
- atom!("-moz-device-orientation"),
- AllowsRanges::No,
- keyword_evaluator!(eval_device_orientation, Orientation),
- FeatureFlags::empty(),
- ),
- // Webkit extensions that we support for de-facto web compatibility.
- // -webkit-{min|max}-device-pixel-ratio (controlled with its own pref):
- feature!(
- atom!("device-pixel-ratio"),
- AllowsRanges::Yes,
- Evaluator::Float(eval_device_pixel_ratio),
- FeatureFlags::WEBKIT_PREFIX,
- ),
- // -webkit-transform-3d.
- feature!(
- atom!("transform-3d"),
- AllowsRanges::No,
- Evaluator::BoolInteger(eval_transform_3d),
- FeatureFlags::WEBKIT_PREFIX,
- ),
- feature!(
- atom!("-moz-device-pixel-ratio"),
- AllowsRanges::Yes,
- Evaluator::Float(eval_device_pixel_ratio),
- FeatureFlags::empty(),
- ),
- feature!(
- atom!("resolution"),
- AllowsRanges::Yes,
- Evaluator::Resolution(eval_resolution),
- FeatureFlags::empty(),
- ),
- feature!(
- atom!("display-mode"),
- AllowsRanges::No,
- keyword_evaluator!(eval_display_mode, DisplayMode),
- FeatureFlags::empty(),
- ),
- feature!(
- atom!("grid"),
- AllowsRanges::No,
- Evaluator::BoolInteger(eval_grid),
- FeatureFlags::empty(),
- ),
- feature!(
- atom!("scan"),
- AllowsRanges::No,
- keyword_evaluator!(eval_scan, Scan),
- FeatureFlags::empty(),
- ),
- feature!(
- atom!("color"),
- AllowsRanges::Yes,
- Evaluator::Integer(eval_color),
- FeatureFlags::empty(),
- ),
- feature!(
- atom!("color-index"),
- AllowsRanges::Yes,
- Evaluator::Integer(eval_color_index),
- FeatureFlags::empty(),
- ),
- feature!(
- atom!("monochrome"),
- AllowsRanges::Yes,
- Evaluator::Integer(eval_monochrome),
- FeatureFlags::empty(),
- ),
- feature!(
- atom!("color-gamut"),
- AllowsRanges::No,
- keyword_evaluator!(eval_color_gamut, ColorGamut),
- FeatureFlags::empty(),
- ),
- feature!(
- atom!("prefers-reduced-motion"),
- AllowsRanges::No,
- keyword_evaluator!(eval_prefers_reduced_motion, PrefersReducedMotion),
- FeatureFlags::empty(),
- ),
- feature!(
- atom!("prefers-reduced-transparency"),
- AllowsRanges::No,
- keyword_evaluator!(
- eval_prefers_reduced_transparency,
- PrefersReducedTransparency
- ),
- FeatureFlags::empty(),
- ),
- feature!(
- atom!("prefers-contrast"),
- AllowsRanges::No,
- keyword_evaluator!(eval_prefers_contrast, PrefersContrast),
- // Note: by default this is only enabled in browser chrome and
- // ua. It can be enabled on the web via the
- // layout.css.prefers-contrast.enabled preference. See
- // disabed_by_pref in media_feature_expression.rs for how that
- // is done.
- FeatureFlags::empty(),
- ),
- feature!(
- atom!("forced-colors"),
- AllowsRanges::No,
- keyword_evaluator!(eval_forced_colors, ForcedColors),
- FeatureFlags::empty(),
- ),
- feature!(
- atom!("inverted-colors"),
- AllowsRanges::No,
- keyword_evaluator!(eval_inverted_colors, InvertedColors),
- FeatureFlags::empty(),
- ),
- feature!(
- atom!("overflow-block"),
- AllowsRanges::No,
- keyword_evaluator!(eval_overflow_block, OverflowBlock),
- FeatureFlags::empty(),
- ),
- feature!(
- atom!("overflow-inline"),
- AllowsRanges::No,
- keyword_evaluator!(eval_overflow_inline, OverflowInline),
- FeatureFlags::empty(),
- ),
- feature!(
- atom!("update"),
- AllowsRanges::No,
- keyword_evaluator!(eval_update, Update),
- FeatureFlags::empty(),
- ),
- feature!(
- atom!("prefers-color-scheme"),
- AllowsRanges::No,
- keyword_evaluator!(eval_prefers_color_scheme, PrefersColorScheme),
- FeatureFlags::empty(),
- ),
- feature!(
- atom!("dynamic-range"),
- AllowsRanges::No,
- keyword_evaluator!(eval_dynamic_range, DynamicRange),
- FeatureFlags::empty(),
- ),
- feature!(
- atom!("video-dynamic-range"),
- AllowsRanges::No,
- keyword_evaluator!(eval_video_dynamic_range, DynamicRange),
- FeatureFlags::empty(),
- ),
- feature!(
- atom!("scripting"),
- AllowsRanges::No,
- keyword_evaluator!(eval_scripting, Scripting),
- FeatureFlags::empty(),
- ),
- // Evaluates to the preferred color scheme for content. Only useful in
- // chrome context, where the chrome color-scheme and the content
- // color-scheme might differ.
- feature!(
- atom!("-moz-content-prefers-color-scheme"),
- AllowsRanges::No,
- keyword_evaluator!(eval_content_prefers_color_scheme, PrefersColorScheme),
- FeatureFlags::CHROME_AND_UA_ONLY,
- ),
- feature!(
- atom!("pointer"),
- AllowsRanges::No,
- keyword_evaluator!(eval_pointer, Pointer),
- FeatureFlags::empty(),
- ),
- feature!(
- atom!("any-pointer"),
- AllowsRanges::No,
- keyword_evaluator!(eval_any_pointer, Pointer),
- FeatureFlags::empty(),
- ),
- feature!(
- atom!("hover"),
- AllowsRanges::No,
- keyword_evaluator!(eval_hover, Hover),
- FeatureFlags::empty(),
- ),
- feature!(
- atom!("any-hover"),
- AllowsRanges::No,
- keyword_evaluator!(eval_any_hover, Hover),
- FeatureFlags::empty(),
- ),
- // Internal -moz-is-glyph media feature: applies only inside SVG glyphs.
- // Internal because it is really only useful in the user agent anyway
- // and therefore not worth standardizing.
- feature!(
- atom!("-moz-is-glyph"),
- AllowsRanges::No,
- Evaluator::BoolInteger(eval_moz_is_glyph),
- FeatureFlags::CHROME_AND_UA_ONLY,
- ),
- feature!(
- atom!("-moz-is-resource-document"),
- AllowsRanges::No,
- Evaluator::BoolInteger(eval_moz_is_resource_document),
- FeatureFlags::CHROME_AND_UA_ONLY,
- ),
- feature!(
- atom!("-moz-platform"),
- AllowsRanges::No,
- keyword_evaluator!(eval_moz_platform, Platform),
- FeatureFlags::CHROME_AND_UA_ONLY,
- ),
- feature!(
- atom!("-moz-print-preview"),
- AllowsRanges::No,
- Evaluator::BoolInteger(eval_moz_print_preview),
- FeatureFlags::CHROME_AND_UA_ONLY,
- ),
- feature!(
- atom!("-moz-non-native-content-theme"),
- AllowsRanges::No,
- Evaluator::BoolInteger(eval_moz_non_native_content_theme),
- FeatureFlags::CHROME_AND_UA_ONLY,
- ),
- feature!(
- atom!("-moz-windows-non-native-menus"),
- AllowsRanges::No,
- Evaluator::BoolInteger(eval_moz_windows_non_native_menus),
- FeatureFlags::CHROME_AND_UA_ONLY,
- ),
- feature!(
- atom!("-moz-overlay-scrollbars"),
- AllowsRanges::No,
- Evaluator::BoolInteger(eval_moz_overlay_scrollbars),
- FeatureFlags::CHROME_AND_UA_ONLY,
- ),
- lnf_int_feature!(
- atom!("-moz-scrollbar-start-backward"),
- ScrollArrowStyle,
- get_scrollbar_start_backward
- ),
- lnf_int_feature!(
- atom!("-moz-scrollbar-start-forward"),
- ScrollArrowStyle,
- get_scrollbar_start_forward
- ),
- lnf_int_feature!(
- atom!("-moz-scrollbar-end-backward"),
- ScrollArrowStyle,
- get_scrollbar_end_backward
- ),
- lnf_int_feature!(
- atom!("-moz-scrollbar-end-forward"),
- ScrollArrowStyle,
- get_scrollbar_end_forward
- ),
- lnf_int_feature!(atom!("-moz-menubar-drag"), MenuBarDrag),
- lnf_int_feature!(atom!("-moz-windows-default-theme"), WindowsDefaultTheme),
- lnf_int_feature!(atom!("-moz-mac-graphite-theme"), MacGraphiteTheme),
- lnf_int_feature!(atom!("-moz-mac-big-sur-theme"), MacBigSurTheme),
- lnf_int_feature!(atom!("-moz-mac-rtl"), MacRTL),
- lnf_int_feature!(
- atom!("-moz-windows-accent-color-in-titlebar"),
- WindowsAccentColorInTitlebar
- ),
- lnf_int_feature!(atom!("-moz-windows-compositor"), DWMCompositor),
- lnf_int_feature!(atom!("-moz-windows-classic"), WindowsClassic),
- lnf_int_feature!(atom!("-moz-windows-glass"), WindowsGlass),
- lnf_int_feature!(atom!("-moz-swipe-animation-enabled"), SwipeAnimationEnabled),
- lnf_int_feature!(atom!("-moz-gtk-csd-available"), GTKCSDAvailable),
- lnf_int_feature!(atom!("-moz-gtk-csd-minimize-button"), GTKCSDMinimizeButton),
- lnf_int_feature!(atom!("-moz-gtk-csd-maximize-button"), GTKCSDMaximizeButton),
- lnf_int_feature!(atom!("-moz-gtk-csd-close-button"), GTKCSDCloseButton),
- lnf_int_feature!(
- atom!("-moz-gtk-csd-reversed-placement"),
- GTKCSDReversedPlacement
- ),
- lnf_int_feature!(atom!("-moz-system-dark-theme"), SystemUsesDarkTheme),
- lnf_int_feature!(atom!("-moz-panel-animations"), PanelAnimations),
- // media query for MathML Core's implementation of maction/semantics
- bool_pref_feature!(
- atom!("-moz-mathml-core-maction-and-semantics"),
- "mathml.legacy_maction_and_semantics_implementations.disabled"
- ),
- // media query for MathML Core's implementation of ms
- bool_pref_feature!(
- atom!("-moz-mathml-core-ms"),
- "mathml.ms_lquote_rquote_attributes.disabled"
- ),
- // media query for popover attribute
- bool_pref_feature!(atom!("-moz-popover-enabled"), "dom.element.popover.enabled"),
-];
diff --git a/components/style/gecko/media_queries.rs b/components/style/gecko/media_queries.rs
deleted file mode 100644
index 2ea4229133a..00000000000
--- a/components/style/gecko/media_queries.rs
+++ /dev/null
@@ -1,560 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
-
-//! Gecko's media-query device and expression representation.
-
-use crate::color::AbsoluteColor;
-use crate::context::QuirksMode;
-use crate::custom_properties::CssEnvironment;
-use crate::font_metrics::FontMetrics;
-use crate::gecko::values::{convert_absolute_color_to_nscolor, convert_nscolor_to_absolute_color};
-use crate::gecko_bindings::bindings;
-use crate::gecko_bindings::structs;
-use crate::media_queries::MediaType;
-use crate::properties::ComputedValues;
-use crate::string_cache::Atom;
-use crate::values::computed::font::GenericFontFamily;
-use crate::values::computed::{ColorScheme, Length, NonNegativeLength};
-use crate::values::specified::color::SystemColor;
-use crate::values::specified::font::FONT_MEDIUM_PX;
-use crate::values::specified::ViewportVariant;
-use crate::values::{CustomIdent, KeyframesName};
-use app_units::{Au, AU_PER_PX};
-use euclid::default::Size2D;
-use euclid::{Scale, SideOffsets2D};
-use servo_arc::Arc;
-use std::sync::atomic::{AtomicBool, AtomicU32, AtomicUsize, Ordering};
-use std::{cmp, fmt};
-use style_traits::{CSSPixel, DevicePixel};
-
-/// The `Device` in Gecko wraps a pres context, has a default values computed,
-/// and contains all the viewport rule state.
-pub struct Device {
- /// NB: The document owns the styleset, who owns the stylist, and thus the
- /// `Device`, so having a raw document pointer here is fine.
- document: *const structs::Document,
- default_values: Arc<ComputedValues>,
- /// The font size of the root element.
- ///
- /// This is set when computing the style of the root element, and used for
- /// rem units in other elements.
- ///
- /// When computing the style of the root element, there can't be any other
- /// style being computed at the same time, given we need the style of the
- /// parent to compute everything else. So it is correct to just use a
- /// relaxed atomic here.
- root_font_size: AtomicU32,
- /// The body text color, stored as an `nscolor`, used for the "tables
- /// inherit from body" quirk.
- ///
- /// <https://quirks.spec.whatwg.org/#the-tables-inherit-color-from-body-quirk>
- body_text_color: AtomicUsize,
- /// Whether any styles computed in the document relied on the root font-size
- /// by using rem units.
- used_root_font_size: AtomicBool,
- /// Whether any styles computed in the document relied on font metrics.
- used_font_metrics: AtomicBool,
- /// Whether any styles computed in the document relied on the viewport size
- /// by using vw/vh/vmin/vmax units.
- used_viewport_size: AtomicBool,
- /// Whether any styles computed in the document relied on the viewport size
- /// by using dvw/dvh/dvmin/dvmax units.
- used_dynamic_viewport_size: AtomicBool,
- /// The CssEnvironment object responsible of getting CSS environment
- /// variables.
- environment: CssEnvironment,
-}
-
-impl fmt::Debug for Device {
- fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
- use nsstring::nsCString;
-
- let mut doc_uri = nsCString::new();
- unsafe {
- bindings::Gecko_nsIURI_Debug((*self.document()).mDocumentURI.raw(), &mut doc_uri)
- };
-
- f.debug_struct("Device")
- .field("document_url", &doc_uri)
- .finish()
- }
-}
-
-unsafe impl Sync for Device {}
-unsafe impl Send for Device {}
-
-impl Device {
- /// Trivially constructs a new `Device`.
- pub fn new(document: *const structs::Document) -> Self {
- assert!(!document.is_null());
- let doc = unsafe { &*document };
- let prefs = unsafe { &*bindings::Gecko_GetPrefSheetPrefs(doc) };
- Device {
- document,
- default_values: ComputedValues::default_values(doc),
- root_font_size: AtomicU32::new(FONT_MEDIUM_PX.to_bits()),
- // This gets updated when we see the <body>, so it doesn't really
- // matter which color-scheme we look at here.
- body_text_color: AtomicUsize::new(prefs.mLightColors.mDefault as usize),
- used_root_font_size: AtomicBool::new(false),
- used_font_metrics: AtomicBool::new(false),
- used_viewport_size: AtomicBool::new(false),
- used_dynamic_viewport_size: AtomicBool::new(false),
- environment: CssEnvironment,
- }
- }
-
- /// Get the relevant environment to resolve `env()` functions.
- #[inline]
- pub fn environment(&self) -> &CssEnvironment {
- &self.environment
- }
-
- /// Returns the computed line-height for the font in a given computed values instance.
- ///
- /// If you pass down an element, then the used line-height is returned.
- pub fn calc_line_height(
- &self,
- line_height: &crate::values::computed::LineHeight,
- vertical: bool,
- font: &crate::properties::style_structs::Font,
- element: Option<super::wrapper::GeckoElement>,
- ) -> NonNegativeLength {
- let pres_context = self.pres_context();
- let au = Au(unsafe {
- bindings::Gecko_CalcLineHeight(
- line_height,
- pres_context.map_or(std::ptr::null(), |pc| pc),
- vertical,
- &**font,
- element.map_or(std::ptr::null(), |e| e.0),
- )
- });
- NonNegativeLength::new(au.to_f32_px())
- }
-
- /// Whether any animation name may be referenced from the style of any
- /// element.
- pub fn animation_name_may_be_referenced(&self, name: &KeyframesName) -> bool {
- let pc = match self.pres_context() {
- Some(pc) => pc,
- None => return false,
- };
-
- unsafe {
- bindings::Gecko_AnimationNameMayBeReferencedFromStyle(pc, name.as_atom().as_ptr())
- }
- }
-
- /// Returns the default computed values as a reference, in order to match
- /// Servo.
- pub fn default_computed_values(&self) -> &ComputedValues {
- &self.default_values
- }
-
- /// Returns the default computed values as an `Arc`.
- pub fn default_computed_values_arc(&self) -> &Arc<ComputedValues> {
- &self.default_values
- }
-
- /// Get the font size of the root element (for rem)
- pub fn root_font_size(&self) -> Length {
- self.used_root_font_size.store(true, Ordering::Relaxed);
- Length::new(f32::from_bits(self.root_font_size.load(Ordering::Relaxed)))
- }
-
- /// Set the font size of the root element (for rem)
- pub fn set_root_font_size(&self, size: Length) {
- self.root_font_size
- .store(size.px().to_bits(), Ordering::Relaxed)
- }
-
- /// The quirks mode of the document.
- pub fn quirks_mode(&self) -> QuirksMode {
- self.document().mCompatMode.into()
- }
-
- /// Sets the body text color for the "inherit color from body" quirk.
- ///
- /// <https://quirks.spec.whatwg.org/#the-tables-inherit-color-from-body-quirk>
- pub fn set_body_text_color(&self, color: AbsoluteColor) {
- self.body_text_color.store(
- convert_absolute_color_to_nscolor(&color) as usize,
- Ordering::Relaxed,
- )
- }
-
- /// Gets the base size given a generic font family and a language.
- pub fn base_size_for_generic(&self, language: &Atom, generic: GenericFontFamily) -> Length {
- unsafe { bindings::Gecko_GetBaseSize(self.document(), language.as_ptr(), generic) }
- }
-
- /// Gets the size of the scrollbar in CSS pixels.
- pub fn scrollbar_inline_size(&self) -> Length {
- let pc = match self.pres_context() {
- Some(pc) => pc,
- // XXX: we could have a more reasonable default perhaps.
- None => return Length::new(0.0),
- };
- Length::new(unsafe { bindings::Gecko_GetScrollbarInlineSize(pc) })
- }
-
- /// Queries font metrics
- pub fn query_font_metrics(
- &self,
- vertical: bool,
- font: &crate::properties::style_structs::Font,
- base_size: Length,
- in_media_query: bool,
- retrieve_math_scales: bool,
- ) -> FontMetrics {
- self.used_font_metrics.store(true, Ordering::Relaxed);
- let pc = match self.pres_context() {
- Some(pc) => pc,
- None => return Default::default(),
- };
- let gecko_metrics = unsafe {
- bindings::Gecko_GetFontMetrics(
- pc,
- vertical,
- &**font,
- base_size,
- // we don't use the user font set in a media query
- !in_media_query,
- retrieve_math_scales,
- )
- };
- FontMetrics {
- x_height: Some(gecko_metrics.mXSize),
- zero_advance_measure: if gecko_metrics.mChSize.px() >= 0. {
- Some(gecko_metrics.mChSize)
- } else {
- None
- },
- cap_height: if gecko_metrics.mCapHeight.px() >= 0. {
- Some(gecko_metrics.mCapHeight)
- } else {
- None
- },
- ic_width: if gecko_metrics.mIcWidth.px() >= 0. {
- Some(gecko_metrics.mIcWidth)
- } else {
- None
- },
- ascent: gecko_metrics.mAscent,
- script_percent_scale_down: if gecko_metrics.mScriptPercentScaleDown > 0. {
- Some(gecko_metrics.mScriptPercentScaleDown)
- } else {
- None
- },
- script_script_percent_scale_down: if gecko_metrics.mScriptScriptPercentScaleDown > 0. {
- Some(gecko_metrics.mScriptScriptPercentScaleDown)
- } else {
- None
- },
- }
- }
-
- /// Returns the body text color.
- pub fn body_text_color(&self) -> AbsoluteColor {
- convert_nscolor_to_absolute_color(self.body_text_color.load(Ordering::Relaxed) as u32)
- }
-
- /// Gets the document pointer.
- #[inline]
- pub fn document(&self) -> &structs::Document {
- unsafe { &*self.document }
- }
-
- /// Gets the pres context associated with this document.
- #[inline]
- pub fn pres_context(&self) -> Option<&structs::nsPresContext> {
- unsafe {
- self.document()
- .mPresShell
- .as_ref()?
- .mPresContext
- .mRawPtr
- .as_ref()
- }
- }
-
- /// Gets the preference stylesheet prefs for our document.
- #[inline]
- pub fn pref_sheet_prefs(&self) -> &structs::PreferenceSheet_Prefs {
- unsafe { &*bindings::Gecko_GetPrefSheetPrefs(self.document()) }
- }
-
- /// Recreates the default computed values.
- pub fn reset_computed_values(&mut self) {
- self.default_values = ComputedValues::default_values(self.document());
- }
-
- /// Rebuild all the cached data.
- pub fn rebuild_cached_data(&mut self) {
- self.reset_computed_values();
- self.used_root_font_size.store(false, Ordering::Relaxed);
- self.used_font_metrics.store(false, Ordering::Relaxed);
- self.used_viewport_size.store(false, Ordering::Relaxed);
- self.used_dynamic_viewport_size
- .store(false, Ordering::Relaxed);
- }
-
- /// Returns whether we ever looked up the root font size of the Device.
- pub fn used_root_font_size(&self) -> bool {
- self.used_root_font_size.load(Ordering::Relaxed)
- }
-
- /// Recreates all the temporary state that the `Device` stores.
- ///
- /// This includes the viewport override from `@viewport` rules, and also the
- /// default computed values.
- pub fn reset(&mut self) {
- self.reset_computed_values();
- }
-
- /// Returns whether this document is in print preview.
- pub fn is_print_preview(&self) -> bool {
- let pc = match self.pres_context() {
- Some(pc) => pc,
- None => return false,
- };
- pc.mType == structs::nsPresContext_nsPresContextType_eContext_PrintPreview
- }
-
- /// Returns the current media type of the device.
- pub fn media_type(&self) -> MediaType {
- let pc = match self.pres_context() {
- Some(pc) => pc,
- None => return MediaType::screen(),
- };
-
- // Gecko allows emulating random media with mMediaEmulationData.mMedium.
- let medium_to_use = if !pc.mMediaEmulationData.mMedium.mRawPtr.is_null() {
- pc.mMediaEmulationData.mMedium.mRawPtr
- } else {
- pc.mMedium as *const structs::nsAtom as *mut _
- };
-
- MediaType(CustomIdent(unsafe { Atom::from_raw(medium_to_use) }))
- }
-
- // It may make sense to account for @page rule margins here somehow, however
- // it's not clear how that'd work, see:
- // https://github.com/w3c/csswg-drafts/issues/5437
- fn page_size_minus_default_margin(&self, pc: &structs::nsPresContext) -> Size2D<Au> {
- debug_assert!(pc.mIsRootPaginatedDocument() != 0);
- let area = &pc.mPageSize;
- let margin = &pc.mDefaultPageMargin;
- let width = area.width - margin.left - margin.right;
- let height = area.height - margin.top - margin.bottom;
- Size2D::new(Au(cmp::max(width, 0)), Au(cmp::max(height, 0)))
- }
-
- /// Returns the current viewport size in app units.
- pub fn au_viewport_size(&self) -> Size2D<Au> {
- let pc = match self.pres_context() {
- Some(pc) => pc,
- None => return Size2D::new(Au(0), Au(0)),
- };
-
- if pc.mIsRootPaginatedDocument() != 0 {
- return self.page_size_minus_default_margin(pc);
- }
-
- let area = &pc.mVisibleArea;
- Size2D::new(Au(area.width), Au(area.height))
- }
-
- /// Returns the current viewport size in app units, recording that it's been
- /// used for viewport unit resolution.
- pub fn au_viewport_size_for_viewport_unit_resolution(
- &self,
- variant: ViewportVariant,
- ) -> Size2D<Au> {
- self.used_viewport_size.store(true, Ordering::Relaxed);
- let pc = match self.pres_context() {
- Some(pc) => pc,
- None => return Size2D::new(Au(0), Au(0)),
- };
-
- if pc.mIsRootPaginatedDocument() != 0 {
- return self.page_size_minus_default_margin(pc);
- }
-
- match variant {
- ViewportVariant::UADefault => {
- let size = &pc.mSizeForViewportUnits;
- Size2D::new(Au(size.width), Au(size.height))
- },
- ViewportVariant::Small => {
- let size = &pc.mVisibleArea;
- Size2D::new(Au(size.width), Au(size.height))
- },
- ViewportVariant::Large => {
- let size = &pc.mVisibleArea;
- // Looks like IntCoordTyped is treated as if it's u32 in Rust.
- debug_assert!(
- /* pc.mDynamicToolbarMaxHeight >=0 && */
- pc.mDynamicToolbarMaxHeight < i32::MAX as u32
- );
- Size2D::new(
- Au(size.width),
- Au(size.height +
- pc.mDynamicToolbarMaxHeight as i32 * pc.mCurAppUnitsPerDevPixel),
- )
- },
- ViewportVariant::Dynamic => {
- self.used_dynamic_viewport_size
- .store(true, Ordering::Relaxed);
- let size = &pc.mVisibleArea;
- // Looks like IntCoordTyped is treated as if it's u32 in Rust.
- debug_assert!(
- /* pc.mDynamicToolbarHeight >=0 && */
- pc.mDynamicToolbarHeight < i32::MAX as u32
- );
- Size2D::new(
- Au(size.width),
- Au(size.height +
- (pc.mDynamicToolbarMaxHeight - pc.mDynamicToolbarHeight) as i32 *
- pc.mCurAppUnitsPerDevPixel),
- )
- },
- }
- }
-
- /// Returns whether we ever looked up the viewport size of the Device.
- pub fn used_viewport_size(&self) -> bool {
- self.used_viewport_size.load(Ordering::Relaxed)
- }
-
- /// Returns whether we ever looked up the dynamic viewport size of the Device.
- pub fn used_dynamic_viewport_size(&self) -> bool {
- self.used_dynamic_viewport_size.load(Ordering::Relaxed)
- }
-
- /// Returns whether font metrics have been queried.
- pub fn used_font_metrics(&self) -> bool {
- self.used_font_metrics.load(Ordering::Relaxed)
- }
-
- /// Returns whether visited styles are enabled.
- pub fn visited_styles_enabled(&self) -> bool {
- unsafe { bindings::Gecko_VisitedStylesEnabled(self.document()) }
- }
-
- /// Returns the number of app units per device pixel we're using currently.
- pub fn app_units_per_device_pixel(&self) -> i32 {
- match self.pres_context() {
- Some(pc) => pc.mCurAppUnitsPerDevPixel,
- None => AU_PER_PX,
- }
- }
-
- /// Returns the device pixel ratio.
- pub fn device_pixel_ratio(&self) -> Scale<f32, CSSPixel, DevicePixel> {
- let pc = match self.pres_context() {
- Some(pc) => pc,
- None => return Scale::new(1.),
- };
-
- if pc.mMediaEmulationData.mDPPX > 0.0 {
- return Scale::new(pc.mMediaEmulationData.mDPPX);
- }
-
- let au_per_dpx = pc.mCurAppUnitsPerDevPixel as f32;
- let au_per_px = AU_PER_PX as f32;
- Scale::new(au_per_px / au_per_dpx)
- }
-
- /// Returns whether document colors are enabled.
- #[inline]
- pub fn use_document_colors(&self) -> bool {
- let doc = self.document();
- if doc.mIsBeingUsedAsImage() {
- return true;
- }
- self.pref_sheet_prefs().mUseDocumentColors
- }
-
- /// Computes a system color and returns it as an nscolor.
- pub(crate) fn system_nscolor(
- &self,
- system_color: SystemColor,
- color_scheme: &ColorScheme,
- ) -> u32 {
- unsafe { bindings::Gecko_ComputeSystemColor(system_color, self.document(), color_scheme) }
- }
-
- /// Returns the default background color.
- ///
- /// This is only for forced-colors/high-contrast, so looking at light colors
- /// is ok.
- pub fn default_background_color(&self) -> AbsoluteColor {
- let normal = ColorScheme::normal();
- convert_nscolor_to_absolute_color(self.system_nscolor(SystemColor::Canvas, &normal))
- }
-
- /// Returns the default foreground color.
- ///
- /// See above for looking at light colors only.
- pub fn default_color(&self) -> AbsoluteColor {
- let normal = ColorScheme::normal();
- convert_nscolor_to_absolute_color(self.system_nscolor(SystemColor::Canvastext, &normal))
- }
-
- /// Returns the current effective text zoom.
- #[inline]
- fn text_zoom(&self) -> f32 {
- let pc = match self.pres_context() {
- Some(pc) => pc,
- None => return 1.,
- };
- pc.mTextZoom
- }
-
- /// Applies text zoom to a font-size or line-height value (see nsStyleFont::ZoomText).
- #[inline]
- pub fn zoom_text(&self, size: Length) -> Length {
- size.scale_by(self.text_zoom())
- }
-
- /// Un-apply text zoom.
- #[inline]
- pub fn unzoom_text(&self, size: Length) -> Length {
- size.scale_by(1. / self.text_zoom())
- }
-
- /// Returns safe area insets
- pub fn safe_area_insets(&self) -> SideOffsets2D<f32, CSSPixel> {
- let pc = match self.pres_context() {
- Some(pc) => pc,
- None => return SideOffsets2D::zero(),
- };
- let mut top = 0.0;
- let mut right = 0.0;
- let mut bottom = 0.0;
- let mut left = 0.0;
- unsafe {
- bindings::Gecko_GetSafeAreaInsets(pc, &mut top, &mut right, &mut bottom, &mut left)
- };
- SideOffsets2D::new(top, right, bottom, left)
- }
-
- /// Returns true if the given MIME type is supported
- pub fn is_supported_mime_type(&self, mime_type: &str) -> bool {
- unsafe {
- bindings::Gecko_IsSupportedImageMimeType(mime_type.as_ptr(), mime_type.len() as u32)
- }
- }
-
- /// Return whether the document is a chrome document.
- ///
- /// This check is consistent with how we enable chrome rules for chrome:// and resource://
- /// stylesheets (and thus chrome:// documents).
- #[inline]
- pub fn chrome_rules_enabled_for_document(&self) -> bool {
- self.document().mChromeRulesEnabled()
- }
-}
diff --git a/components/style/gecko/mod.rs b/components/style/gecko/mod.rs
deleted file mode 100644
index c32ded14f33..00000000000
--- a/components/style/gecko/mod.rs
+++ /dev/null
@@ -1,23 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
-
-//! Gecko-specific style-system bits.
-
-#[macro_use]
-mod non_ts_pseudo_class_list;
-
-pub mod arc_types;
-pub mod conversions;
-pub mod data;
-pub mod media_features;
-pub mod media_queries;
-pub mod pseudo_element;
-pub mod restyle_damage;
-pub mod selector_parser;
-pub mod snapshot;
-pub mod snapshot_helpers;
-pub mod traversal;
-pub mod url;
-pub mod values;
-pub mod wrapper;
diff --git a/components/style/gecko/non_ts_pseudo_class_list.rs b/components/style/gecko/non_ts_pseudo_class_list.rs
deleted file mode 100644
index e494082047f..00000000000
--- a/components/style/gecko/non_ts_pseudo_class_list.rs
+++ /dev/null
@@ -1,100 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
-
-/*
- * This file contains a helper macro includes all supported non-tree-structural
- * pseudo-classes.
- *
- * FIXME: Find a way to autogenerate this file.
- *
- * Expected usage is as follows:
- * ```
- * macro_rules! pseudo_class_macro{
- * ([$(($css:expr, $name:ident, $gecko_type:tt, $state:tt, $flags:tt),)*]) => {
- * // do stuff
- * }
- * }
- * apply_non_ts_list!(pseudo_class_macro)
- * ```
- *
- * $gecko_type can be either "_" or an ident in Gecko's CSSPseudoClassType.
- * $state can be either "_" or an expression of type ElementState. If present,
- * the semantics are that the pseudo-class matches if any of the bits in
- * $state are set on the element.
- * $flags can be either "_" or an expression of type NonTSPseudoClassFlag,
- * see selector_parser.rs for more details.
- */
-
-macro_rules! apply_non_ts_list {
- ($apply_macro:ident) => {
- $apply_macro! {
- [
- ("-moz-table-border-nonzero", MozTableBorderNonzero, _, PSEUDO_CLASS_ENABLED_IN_UA_SHEETS),
- ("-moz-browser-frame", MozBrowserFrame, _, PSEUDO_CLASS_ENABLED_IN_UA_SHEETS_AND_CHROME),
- ("-moz-select-list-box", MozSelectListBox, _, PSEUDO_CLASS_ENABLED_IN_UA_SHEETS),
- ("link", Link, UNVISITED, _),
- ("any-link", AnyLink, VISITED_OR_UNVISITED, _),
- ("visited", Visited, VISITED, _),
- ("active", Active, ACTIVE, _),
- ("autofill", Autofill, AUTOFILL, _),
- ("checked", Checked, CHECKED, _),
- ("defined", Defined, DEFINED, _),
- ("disabled", Disabled, DISABLED, _),
- ("enabled", Enabled, ENABLED, _),
- ("focus", Focus, FOCUS, _),
- ("focus-within", FocusWithin, FOCUS_WITHIN, _),
- ("focus-visible", FocusVisible, FOCUSRING, _),
- ("hover", Hover, HOVER, _),
- ("-moz-drag-over", MozDragOver, DRAGOVER, _),
- ("target", Target, URLTARGET, _),
- ("indeterminate", Indeterminate, INDETERMINATE, _),
- ("-moz-inert", MozInert, INERT, PSEUDO_CLASS_ENABLED_IN_UA_SHEETS),
- ("-moz-devtools-highlighted", MozDevtoolsHighlighted, DEVTOOLS_HIGHLIGHTED, PSEUDO_CLASS_ENABLED_IN_UA_SHEETS),
- ("-moz-styleeditor-transitioning", MozStyleeditorTransitioning, STYLEEDITOR_TRANSITIONING, PSEUDO_CLASS_ENABLED_IN_UA_SHEETS),
- ("fullscreen", Fullscreen, FULLSCREEN, _),
- ("modal", Modal, MODAL, _),
- ("-moz-topmost-modal", MozTopmostModal, TOPMOST_MODAL, PSEUDO_CLASS_ENABLED_IN_UA_SHEETS),
- ("-moz-broken", MozBroken, BROKEN, _),
- ("-moz-loading", MozLoading, LOADING, _),
- ("-moz-has-dir-attr", MozHasDirAttr, HAS_DIR_ATTR, PSEUDO_CLASS_ENABLED_IN_UA_SHEETS),
- ("-moz-dir-attr-ltr", MozDirAttrLTR, HAS_DIR_ATTR_LTR, PSEUDO_CLASS_ENABLED_IN_UA_SHEETS),
- ("-moz-dir-attr-rtl", MozDirAttrRTL, HAS_DIR_ATTR_RTL, PSEUDO_CLASS_ENABLED_IN_UA_SHEETS),
- ("-moz-dir-attr-like-auto", MozDirAttrLikeAuto, HAS_DIR_ATTR_LIKE_AUTO, PSEUDO_CLASS_ENABLED_IN_UA_SHEETS),
-
- ("-moz-autofill-preview", MozAutofillPreview, AUTOFILL_PREVIEW, PSEUDO_CLASS_ENABLED_IN_UA_SHEETS_AND_CHROME),
- ("-moz-value-empty", MozValueEmpty, VALUE_EMPTY, PSEUDO_CLASS_ENABLED_IN_UA_SHEETS),
- ("-moz-revealed", MozRevealed, REVEALED, PSEUDO_CLASS_ENABLED_IN_UA_SHEETS),
-
- ("-moz-math-increment-script-level", MozMathIncrementScriptLevel, INCREMENT_SCRIPT_LEVEL, _),
-
- ("required", Required, REQUIRED, _),
- ("popover-open", PopoverOpen, POPOVER_OPEN, PSEUDO_CLASS_ENABLED_IN_UA_SHEETS_AND_CHROME),
- ("optional", Optional, OPTIONAL_, _),
- ("valid", Valid, VALID, _),
- ("invalid", Invalid, INVALID, _),
- ("in-range", InRange, INRANGE, _),
- ("out-of-range", OutOfRange, OUTOFRANGE, _),
- ("default", Default, DEFAULT, _),
- ("placeholder-shown", PlaceholderShown, PLACEHOLDER_SHOWN, _),
- ("read-only", ReadOnly, READONLY, _),
- ("read-write", ReadWrite, READWRITE, _),
- ("user-valid", UserValid, USER_VALID, _),
- ("user-invalid", UserInvalid, USER_INVALID, _),
- ("-moz-meter-optimum", MozMeterOptimum, OPTIMUM, _),
- ("-moz-meter-sub-optimum", MozMeterSubOptimum, SUB_OPTIMUM, _),
- ("-moz-meter-sub-sub-optimum", MozMeterSubSubOptimum, SUB_SUB_OPTIMUM, _),
-
- ("-moz-first-node", MozFirstNode, _, _),
- ("-moz-last-node", MozLastNode, _, _),
- ("-moz-only-whitespace", MozOnlyWhitespace, _, _),
- ("-moz-native-anonymous", MozNativeAnonymous, _, PSEUDO_CLASS_ENABLED_IN_UA_SHEETS),
- ("-moz-use-shadow-tree-root", MozUseShadowTreeRoot, _, PSEUDO_CLASS_ENABLED_IN_UA_SHEETS),
- ("-moz-is-html", MozIsHTML, _, _),
- ("-moz-placeholder", MozPlaceholder, _, _),
- ("-moz-lwtheme", MozLWTheme, _, PSEUDO_CLASS_ENABLED_IN_UA_SHEETS_AND_CHROME),
- ("-moz-window-inactive", MozWindowInactive, _, _),
- ]
- }
- }
-}
diff --git a/components/style/gecko/pseudo_element.rs b/components/style/gecko/pseudo_element.rs
deleted file mode 100644
index d0c47d51a41..00000000000
--- a/components/style/gecko/pseudo_element.rs
+++ /dev/null
@@ -1,237 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
-
-//! Gecko's definition of a pseudo-element.
-//!
-//! Note that a few autogenerated bits of this live in
-//! `pseudo_element_definition.mako.rs`. If you touch that file, you probably
-//! need to update the checked-in files for Servo.
-
-use crate::gecko_bindings::structs::{self, PseudoStyleType};
-use crate::properties::longhands::display::computed_value::T as Display;
-use crate::properties::{ComputedValues, PropertyFlags};
-use crate::selector_parser::{PseudoElementCascadeType, SelectorImpl};
-use crate::str::{starts_with_ignore_ascii_case, string_as_ascii_lowercase};
-use crate::string_cache::Atom;
-use crate::values::serialize_atom_identifier;
-use crate::values::AtomIdent;
-use cssparser::ToCss;
-use static_prefs::pref;
-use std::fmt;
-
-include!(concat!(
- env!("OUT_DIR"),
- "/gecko/pseudo_element_definition.rs"
-));
-
-impl ::selectors::parser::PseudoElement for PseudoElement {
- type Impl = SelectorImpl;
-
- // ::slotted() should support all tree-abiding pseudo-elements, see
- // https://drafts.csswg.org/css-scoping/#slotted-pseudo
- // https://drafts.csswg.org/css-pseudo-4/#treelike
- #[inline]
- fn valid_after_slotted(&self) -> bool {
- matches!(
- *self,
- PseudoElement::Before |
- PseudoElement::After |
- PseudoElement::Marker |
- PseudoElement::Placeholder |
- PseudoElement::FileSelectorButton
- )
- }
-
- #[inline]
- fn accepts_state_pseudo_classes(&self) -> bool {
- self.supports_user_action_state()
- }
-}
-
-impl PseudoElement {
- /// Returns the kind of cascade type that a given pseudo is going to use.
- ///
- /// In Gecko we only compute ::before and ::after eagerly. We save the rules
- /// for anonymous boxes separately, so we resolve them as precomputed
- /// pseudos.
- ///
- /// We resolve the others lazily, see `Servo_ResolvePseudoStyle`.
- pub fn cascade_type(&self) -> PseudoElementCascadeType {
- if self.is_eager() {
- debug_assert!(!self.is_anon_box());
- return PseudoElementCascadeType::Eager;
- }
-
- if self.is_precomputed() {
- return PseudoElementCascadeType::Precomputed;
- }
-
- PseudoElementCascadeType::Lazy
- }
-
- /// Whether the pseudo-element should inherit from the default computed
- /// values instead of from the parent element.
- ///
- /// This is not the common thing, but there are some pseudos (namely:
- /// ::backdrop), that shouldn't inherit from the parent element.
- pub fn inherits_from_default_values(&self) -> bool {
- matches!(*self, PseudoElement::Backdrop)
- }
-
- /// Gets the canonical index of this eagerly-cascaded pseudo-element.
- #[inline]
- pub fn eager_index(&self) -> usize {
- EAGER_PSEUDOS
- .iter()
- .position(|p| p == self)
- .expect("Not an eager pseudo")
- }
-
- /// Creates a pseudo-element from an eager index.
- #[inline]
- pub fn from_eager_index(i: usize) -> Self {
- EAGER_PSEUDOS[i].clone()
- }
-
- /// Whether animations for the current pseudo element are stored in the
- /// parent element.
- #[inline]
- pub fn animations_stored_in_parent(&self) -> bool {
- matches!(*self, Self::Before | Self::After | Self::Marker)
- }
-
- /// Whether the current pseudo element is ::before or ::after.
- #[inline]
- pub fn is_before_or_after(&self) -> bool {
- self.is_before() || self.is_after()
- }
-
- /// Whether this pseudo-element is the ::before pseudo.
- #[inline]
- pub fn is_before(&self) -> bool {
- *self == PseudoElement::Before
- }
-
- /// Whether this pseudo-element is the ::after pseudo.
- #[inline]
- pub fn is_after(&self) -> bool {
- *self == PseudoElement::After
- }
-
- /// Whether this pseudo-element is the ::marker pseudo.
- #[inline]
- pub fn is_marker(&self) -> bool {
- *self == PseudoElement::Marker
- }
-
- /// Whether this pseudo-element is the ::selection pseudo.
- #[inline]
- pub fn is_selection(&self) -> bool {
- *self == PseudoElement::Selection
- }
-
- /// Whether this pseudo-element is ::first-letter.
- #[inline]
- pub fn is_first_letter(&self) -> bool {
- *self == PseudoElement::FirstLetter
- }
-
- /// Whether this pseudo-element is ::first-line.
- #[inline]
- pub fn is_first_line(&self) -> bool {
- *self == PseudoElement::FirstLine
- }
-
- /// Whether this pseudo-element is the ::-moz-color-swatch pseudo.
- #[inline]
- pub fn is_color_swatch(&self) -> bool {
- *self == PseudoElement::MozColorSwatch
- }
-
- /// Whether this pseudo-element is lazily-cascaded.
- #[inline]
- pub fn is_lazy(&self) -> bool {
- !self.is_eager() && !self.is_precomputed()
- }
-
- /// The identifier of the highlight this pseudo-element represents.
- pub fn highlight_name(&self) -> Option<&AtomIdent> {
- match &*self {
- PseudoElement::Highlight(name) => Some(&name),
- _ => None,
- }
- }
-
- /// Whether this pseudo-element is the ::highlight pseudo.
- pub fn is_highlight(&self) -> bool {
- matches!(*self, PseudoElement::Highlight(_))
- }
-
- /// Whether this pseudo-element supports user action selectors.
- pub fn supports_user_action_state(&self) -> bool {
- (self.flags() & structs::CSS_PSEUDO_ELEMENT_SUPPORTS_USER_ACTION_STATE) != 0
- }
-
- /// Whether this pseudo-element is enabled for all content.
- pub fn enabled_in_content(&self) -> bool {
- if self.is_highlight() && !pref!("dom.customHighlightAPI.enabled") {
- return false;
- }
- return self.flags() & structs::CSS_PSEUDO_ELEMENT_ENABLED_IN_UA_SHEETS_AND_CHROME == 0;
- }
-
- /// Whether this pseudo is enabled explicitly in UA sheets.
- pub fn enabled_in_ua_sheets(&self) -> bool {
- (self.flags() & structs::CSS_PSEUDO_ELEMENT_ENABLED_IN_UA_SHEETS) != 0
- }
-
- /// Whether this pseudo is enabled explicitly in chrome sheets.
- pub fn enabled_in_chrome(&self) -> bool {
- (self.flags() & structs::CSS_PSEUDO_ELEMENT_ENABLED_IN_CHROME) != 0
- }
-
- /// Whether this pseudo-element skips flex/grid container display-based
- /// fixup.
- #[inline]
- pub fn skip_item_display_fixup(&self) -> bool {
- (self.flags() & structs::CSS_PSEUDO_ELEMENT_IS_FLEX_OR_GRID_ITEM) == 0
- }
-
- /// Whether this pseudo-element is precomputed.
- #[inline]
- pub fn is_precomputed(&self) -> bool {
- self.is_anon_box() && !self.is_tree_pseudo_element()
- }
-
- /// Property flag that properties must have to apply to this pseudo-element.
- #[inline]
- pub fn property_restriction(&self) -> Option<PropertyFlags> {
- Some(match *self {
- PseudoElement::FirstLetter => PropertyFlags::APPLIES_TO_FIRST_LETTER,
- PseudoElement::FirstLine => PropertyFlags::APPLIES_TO_FIRST_LINE,
- PseudoElement::Placeholder => PropertyFlags::APPLIES_TO_PLACEHOLDER,
- PseudoElement::Cue => PropertyFlags::APPLIES_TO_CUE,
- PseudoElement::Marker if static_prefs::pref!("layout.css.marker.restricted") => {
- PropertyFlags::APPLIES_TO_MARKER
- },
- _ => return None,
- })
- }
-
- /// Whether this pseudo-element should actually exist if it has
- /// the given styles.
- pub fn should_exist(&self, style: &ComputedValues) -> bool {
- debug_assert!(self.is_eager());
-
- if style.get_box().clone_display() == Display::None {
- return false;
- }
-
- if self.is_before_or_after() && style.ineffective_content_property() {
- return false;
- }
-
- true
- }
-}
diff --git a/components/style/gecko/pseudo_element_definition.mako.rs b/components/style/gecko/pseudo_element_definition.mako.rs
deleted file mode 100644
index 73e7893c998..00000000000
--- a/components/style/gecko/pseudo_element_definition.mako.rs
+++ /dev/null
@@ -1,276 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
-
-/// Gecko's pseudo-element definition.
-///
-/// We intentionally double-box legacy ::-moz-tree pseudo-elements to keep the
-/// size of PseudoElement (and thus selector components) small.
-#[derive(Clone, Debug, Eq, Hash, MallocSizeOf, PartialEq, ToShmem)]
-pub enum PseudoElement {
- % for pseudo in PSEUDOS:
- /// ${pseudo.value}
- % if pseudo.is_tree_pseudo_element():
- ${pseudo.capitalized_pseudo()}(thin_vec::ThinVec<Atom>),
- % elif pseudo.pseudo_ident == "highlight":
- ${pseudo.capitalized_pseudo()}(AtomIdent),
- % else:
- ${pseudo.capitalized_pseudo()},
- % endif
- % endfor
- /// ::-webkit-* that we don't recognize
- /// https://github.com/whatwg/compat/issues/103
- UnknownWebkit(Atom),
-}
-
-/// Important: If you change this, you should also update Gecko's
-/// nsCSSPseudoElements::IsEagerlyCascadedInServo.
-<% EAGER_PSEUDOS = ["Before", "After", "FirstLine", "FirstLetter"] %>
-<% TREE_PSEUDOS = [pseudo for pseudo in PSEUDOS if pseudo.is_tree_pseudo_element()] %>
-<% SIMPLE_PSEUDOS = [pseudo for pseudo in PSEUDOS if pseudo.is_simple_pseudo_element()] %>
-
-/// The number of eager pseudo-elements.
-pub const EAGER_PSEUDO_COUNT: usize = ${len(EAGER_PSEUDOS)};
-
-/// The number of non-functional pseudo-elements.
-pub const SIMPLE_PSEUDO_COUNT: usize = ${len(SIMPLE_PSEUDOS)};
-
-/// The number of tree pseudo-elements.
-pub const TREE_PSEUDO_COUNT: usize = ${len(TREE_PSEUDOS)};
-
-/// The number of all pseudo-elements.
-pub const PSEUDO_COUNT: usize = ${len(PSEUDOS)};
-
-/// The list of eager pseudos.
-pub const EAGER_PSEUDOS: [PseudoElement; EAGER_PSEUDO_COUNT] = [
- % for eager_pseudo_name in EAGER_PSEUDOS:
- PseudoElement::${eager_pseudo_name},
- % endfor
-];
-
-<%def name="pseudo_element_variant(pseudo, tree_arg='..')">\
-PseudoElement::${pseudo.capitalized_pseudo()}${"({})".format(tree_arg) if not pseudo.is_simple_pseudo_element() else ""}\
-</%def>
-
-impl PseudoElement {
- /// Returns an index of the pseudo-element.
- #[inline]
- pub fn index(&self) -> usize {
- match *self {
- % for i, pseudo in enumerate(PSEUDOS):
- ${pseudo_element_variant(pseudo)} => ${i},
- % endfor
- PseudoElement::UnknownWebkit(..) => unreachable!(),
- }
- }
-
- /// Returns an array of `None` values.
- ///
- /// FIXME(emilio): Integer generics can't come soon enough.
- pub fn pseudo_none_array<T>() -> [Option<T>; PSEUDO_COUNT] {
- [
- ${",\n ".join(["None" for pseudo in PSEUDOS])}
- ]
- }
-
- /// Whether this pseudo-element is an anonymous box.
- #[inline]
- pub fn is_anon_box(&self) -> bool {
- match *self {
- % for pseudo in PSEUDOS:
- % if pseudo.is_anon_box():
- ${pseudo_element_variant(pseudo)} => true,
- % endif
- % endfor
- _ => false,
- }
- }
-
- /// Whether this pseudo-element is eagerly-cascaded.
- #[inline]
- pub fn is_eager(&self) -> bool {
- matches!(*self,
- ${" | ".join(map(lambda name: "PseudoElement::{}".format(name), EAGER_PSEUDOS))})
- }
-
- /// Whether this pseudo-element is tree pseudo-element.
- #[inline]
- pub fn is_tree_pseudo_element(&self) -> bool {
- match *self {
- % for pseudo in TREE_PSEUDOS:
- ${pseudo_element_variant(pseudo)} => true,
- % endfor
- _ => false,
- }
- }
-
- /// Whether this pseudo-element is an unknown Webkit-prefixed pseudo-element.
- #[inline]
- pub fn is_unknown_webkit_pseudo_element(&self) -> bool {
- matches!(*self, PseudoElement::UnknownWebkit(..))
- }
-
- /// Gets the flags associated to this pseudo-element, or 0 if it's an
- /// anonymous box.
- pub fn flags(&self) -> u32 {
- match *self {
- % for pseudo in PSEUDOS:
- ${pseudo_element_variant(pseudo)} =>
- % if pseudo.is_tree_pseudo_element():
- structs::CSS_PSEUDO_ELEMENT_ENABLED_IN_UA_SHEETS_AND_CHROME,
- % elif pseudo.is_anon_box():
- structs::CSS_PSEUDO_ELEMENT_ENABLED_IN_UA_SHEETS,
- % else:
- structs::SERVO_CSS_PSEUDO_ELEMENT_FLAGS_${pseudo.pseudo_ident},
- % endif
- % endfor
- PseudoElement::UnknownWebkit(..) => 0,
- }
- }
-
- /// Construct a pseudo-element from a `PseudoStyleType`.
- #[inline]
- pub fn from_pseudo_type(type_: PseudoStyleType) -> Option<Self> {
- match type_ {
- % for pseudo in PSEUDOS:
- % if pseudo.is_simple_pseudo_element():
- PseudoStyleType::${pseudo.pseudo_ident} => {
- Some(${pseudo_element_variant(pseudo)})
- },
- % endif
- % endfor
- _ => None,
- }
- }
-
- /// Construct a `PseudoStyleType` from a pseudo-element
- #[inline]
- pub fn pseudo_type(&self) -> PseudoStyleType {
- match *self {
- % for pseudo in PSEUDOS:
- % if pseudo.is_tree_pseudo_element():
- PseudoElement::${pseudo.capitalized_pseudo()}(..) => PseudoStyleType::XULTree,
- % elif pseudo.pseudo_ident == "highlight":
- PseudoElement::${pseudo.capitalized_pseudo()}(..) => PseudoStyleType::${pseudo.pseudo_ident},
- % else:
- PseudoElement::${pseudo.capitalized_pseudo()} => PseudoStyleType::${pseudo.pseudo_ident},
- % endif
- % endfor
- PseudoElement::UnknownWebkit(..) => unreachable!(),
- }
- }
-
- /// Get the argument list of a tree pseudo-element.
- #[inline]
- pub fn tree_pseudo_args(&self) -> Option<<&[Atom]> {
- match *self {
- % for pseudo in TREE_PSEUDOS:
- PseudoElement::${pseudo.capitalized_pseudo()}(ref args) => Some(args),
- % endfor
- _ => None,
- }
- }
-
- /// Construct a tree pseudo-element from atom and args.
- #[inline]
- pub fn from_tree_pseudo_atom(atom: &Atom, args: Box<[Atom]>) -> Option<Self> {
- % for pseudo in PSEUDOS:
- % if pseudo.is_tree_pseudo_element():
- if atom == &atom!("${pseudo.value}") {
- return Some(PseudoElement::${pseudo.capitalized_pseudo()}(args.into()));
- }
- % endif
- % endfor
- None
- }
-
- /// Constructs a pseudo-element from a string of text.
- ///
- /// Returns `None` if the pseudo-element is not recognised.
- #[inline]
- pub fn from_slice(name: &str, allow_unkown_webkit: bool) -> Option<Self> {
- // We don't need to support tree pseudos because functional
- // pseudo-elements needs arguments, and thus should be created
- // via other methods.
- match_ignore_ascii_case! { name,
- % for pseudo in SIMPLE_PSEUDOS:
- "${pseudo.value[1:]}" => {
- return Some(${pseudo_element_variant(pseudo)})
- },
- % endfor
- // Alias some legacy prefixed pseudos to their standardized name at parse time:
- "-moz-selection" => {
- return Some(PseudoElement::Selection);
- },
- "-moz-placeholder" => {
- return Some(PseudoElement::Placeholder);
- },
- "-moz-list-bullet" | "-moz-list-number" => {
- return Some(PseudoElement::Marker);
- },
- _ => {
- if starts_with_ignore_ascii_case(name, "-moz-tree-") {
- return PseudoElement::tree_pseudo_element(name, Default::default())
- }
- const WEBKIT_PREFIX: &str = "-webkit-";
- if allow_unkown_webkit && starts_with_ignore_ascii_case(name, WEBKIT_PREFIX) {
- let part = string_as_ascii_lowercase(&name[WEBKIT_PREFIX.len()..]);
- return Some(PseudoElement::UnknownWebkit(part.into()));
- }
- }
- }
-
- None
- }
-
- /// Constructs a tree pseudo-element from the given name and arguments.
- /// "name" must start with "-moz-tree-".
- ///
- /// Returns `None` if the pseudo-element is not recognized.
- #[inline]
- pub fn tree_pseudo_element(name: &str, args: thin_vec::ThinVec<Atom>) -> Option<Self> {
- debug_assert!(starts_with_ignore_ascii_case(name, "-moz-tree-"));
- let tree_part = &name[10..];
- % for pseudo in TREE_PSEUDOS:
- if tree_part.eq_ignore_ascii_case("${pseudo.value[11:]}") {
- return Some(${pseudo_element_variant(pseudo, "args")});
- }
- % endfor
- None
- }
-}
-
-impl ToCss for PseudoElement {
- fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
- dest.write_char(':')?;
- match *self {
- % for pseudo in (p for p in PSEUDOS if p.pseudo_ident != "highlight"):
- ${pseudo_element_variant(pseudo)} => dest.write_str("${pseudo.value}")?,
- % endfor
- PseudoElement::Highlight(ref name) => {
- dest.write_str(":highlight(")?;
- serialize_atom_identifier(name, dest)?;
- dest.write_char(')')?;
- }
- PseudoElement::UnknownWebkit(ref atom) => {
- dest.write_str(":-webkit-")?;
- serialize_atom_identifier(atom, dest)?;
- }
- }
- if let Some(args) = self.tree_pseudo_args() {
- if !args.is_empty() {
- dest.write_char('(')?;
- let mut iter = args.iter();
- if let Some(first) = iter.next() {
- serialize_atom_identifier(&first, dest)?;
- for item in iter {
- dest.write_str(", ")?;
- serialize_atom_identifier(item, dest)?;
- }
- }
- dest.write_char(')')?;
- }
- }
- Ok(())
- }
-}
diff --git a/components/style/gecko/regen_atoms.py b/components/style/gecko/regen_atoms.py
deleted file mode 100755
index 61f2fc4c635..00000000000
--- a/components/style/gecko/regen_atoms.py
+++ /dev/null
@@ -1,218 +0,0 @@
-#!/usr/bin/env python
-
-# This Source Code Form is subject to the terms of the Mozilla Public
-# License, v. 2.0. If a copy of the MPL was not distributed with this
-# file, You can obtain one at https://mozilla.org/MPL/2.0/.
-
-import re
-import os
-import sys
-
-from io import BytesIO
-
-GECKO_DIR = os.path.dirname(__file__.replace("\\", "/"))
-sys.path.insert(0, os.path.join(os.path.dirname(GECKO_DIR), "properties"))
-
-import build
-
-
-# Matches lines like `GK_ATOM(foo, "foo", 0x12345678, true, nsStaticAtom, PseudoElementAtom)`.
-PATTERN = re.compile(
- '^GK_ATOM\(([^,]*),[^"]*"([^"]*)",\s*(0x[0-9a-f]+),\s*[^,]*,\s*([^,]*),\s*([^)]*)\)',
- re.MULTILINE,
-)
-FILE = "include/nsGkAtomList.h"
-
-
-def map_atom(ident):
- if ident in {
- "box",
- "loop",
- "match",
- "mod",
- "ref",
- "self",
- "type",
- "use",
- "where",
- "in",
- }:
- return ident + "_"
- return ident
-
-
-class Atom:
- def __init__(self, ident, value, hash, ty, atom_type):
- self.ident = "nsGkAtoms_{}".format(ident)
- self.original_ident = ident
- self.value = value
- self.hash = hash
- # The Gecko type: "nsStaticAtom", "nsCSSPseudoElementStaticAtom", or
- # "nsAnonBoxPseudoStaticAtom".
- self.ty = ty
- # The type of atom: "Atom", "PseudoElement", "NonInheritingAnonBox",
- # or "InheritingAnonBox".
- self.atom_type = atom_type
-
- if (
- self.is_pseudo_element()
- or self.is_anon_box()
- or self.is_tree_pseudo_element()
- ):
- self.pseudo_ident = (ident.split("_", 1))[1]
-
- if self.is_anon_box():
- assert self.is_inheriting_anon_box() or self.is_non_inheriting_anon_box()
-
- def type(self):
- return self.ty
-
- def capitalized_pseudo(self):
- return self.pseudo_ident[0].upper() + self.pseudo_ident[1:]
-
- def is_pseudo_element(self):
- return self.atom_type == "PseudoElementAtom"
-
- def is_anon_box(self):
- if self.is_tree_pseudo_element():
- return False
- return self.is_non_inheriting_anon_box() or self.is_inheriting_anon_box()
-
- def is_non_inheriting_anon_box(self):
- assert not self.is_tree_pseudo_element()
- return self.atom_type == "NonInheritingAnonBoxAtom"
-
- def is_inheriting_anon_box(self):
- if self.is_tree_pseudo_element():
- return False
- return self.atom_type == "InheritingAnonBoxAtom"
-
- def is_tree_pseudo_element(self):
- return self.value.startswith(":-moz-tree-")
-
- def is_simple_pseudo_element(self) -> bool:
- return not (self.is_tree_pseudo_element() or self.pseudo_ident == "highlight")
-
-
-def collect_atoms(objdir):
- atoms = []
- path = os.path.abspath(os.path.join(objdir, FILE))
- print("cargo:rerun-if-changed={}".format(path))
- with open(path) as f:
- content = f.read()
- for result in PATTERN.finditer(content):
- atoms.append(
- Atom(
- result.group(1),
- result.group(2),
- result.group(3),
- result.group(4),
- result.group(5),
- )
- )
- return atoms
-
-
-class FileAvoidWrite(BytesIO):
- """File-like object that buffers output and only writes if content changed."""
-
- def __init__(self, filename):
- BytesIO.__init__(self)
- self.name = filename
-
- def write(self, buf):
- if isinstance(buf, str):
- buf = buf.encode("utf-8")
- BytesIO.write(self, buf)
-
- def close(self):
- buf = self.getvalue()
- BytesIO.close(self)
- try:
- with open(self.name, "rb") as f:
- old_content = f.read()
- if old_content == buf:
- print("{} is not changed, skip".format(self.name))
- return
- except IOError:
- pass
- with open(self.name, "wb") as f:
- f.write(buf)
-
- def __enter__(self):
- return self
-
- def __exit__(self, type, value, traceback):
- if not self.closed:
- self.close()
-
-
-PRELUDE = """
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
-
-// Autogenerated file created by components/style/gecko/regen_atoms.py.
-// DO NOT EDIT DIRECTLY
-"""[
- 1:
-]
-
-RULE_TEMPLATE = """
- ("{atom}") => {{{{
- #[allow(unsafe_code)] #[allow(unused_unsafe)]
- unsafe {{ $crate::string_cache::Atom::from_index_unchecked({index}) }}
- }}}};
-"""[
- 1:
-]
-
-MACRO_TEMPLATE = """
-/// Returns a static atom by passing the literal string it represents.
-#[macro_export]
-macro_rules! atom {{
-{body}\
-}}
-"""
-
-
-def write_atom_macro(atoms, file_name):
- with FileAvoidWrite(file_name) as f:
- f.write(PRELUDE)
- macro_rules = [
- RULE_TEMPLATE.format(atom=atom.value, name=atom.ident, index=i)
- for (i, atom) in enumerate(atoms)
- ]
- f.write(MACRO_TEMPLATE.format(body="".join(macro_rules)))
-
-
-def write_pseudo_elements(atoms, target_filename):
- pseudos = []
- for atom in atoms:
- if (
- atom.type() == "nsCSSPseudoElementStaticAtom"
- or atom.type() == "nsCSSAnonBoxPseudoStaticAtom"
- ):
- pseudos.append(atom)
-
- pseudo_definition_template = os.path.join(
- GECKO_DIR, "pseudo_element_definition.mako.rs"
- )
- print("cargo:rerun-if-changed={}".format(pseudo_definition_template))
- contents = build.render(pseudo_definition_template, PSEUDOS=pseudos)
-
- with FileAvoidWrite(target_filename) as f:
- f.write(contents)
-
-
-def generate_atoms(dist, out):
- atoms = collect_atoms(dist)
- write_atom_macro(atoms, os.path.join(out, "atom_macro.rs"))
- write_pseudo_elements(atoms, os.path.join(out, "pseudo_element_definition.rs"))
-
-
-if __name__ == "__main__":
- if len(sys.argv) != 3:
- print("Usage: {} dist out".format(sys.argv[0]))
- exit(2)
- generate_atoms(sys.argv[1], sys.argv[2])
diff --git a/components/style/gecko/restyle_damage.rs b/components/style/gecko/restyle_damage.rs
deleted file mode 100644
index 4749daea183..00000000000
--- a/components/style/gecko/restyle_damage.rs
+++ /dev/null
@@ -1,121 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
-
-//! Gecko's restyle damage computation (aka change hints, aka `nsChangeHint`).
-
-use crate::gecko_bindings::bindings;
-use crate::gecko_bindings::structs;
-use crate::gecko_bindings::structs::nsChangeHint;
-use crate::matching::{StyleChange, StyleDifference};
-use crate::properties::ComputedValues;
-use std::ops::{BitAnd, BitOr, BitOrAssign, Not};
-
-/// The representation of Gecko's restyle damage is just a wrapper over
-/// `nsChangeHint`.
-#[derive(Clone, Copy, Debug, PartialEq)]
-pub struct GeckoRestyleDamage(nsChangeHint);
-
-impl GeckoRestyleDamage {
- /// Trivially construct a new `GeckoRestyleDamage`.
- #[inline]
- pub fn new(raw: nsChangeHint) -> Self {
- GeckoRestyleDamage(raw)
- }
-
- /// Get the inner change hint for this damage.
- #[inline]
- pub fn as_change_hint(&self) -> nsChangeHint {
- self.0
- }
-
- /// Get an empty change hint, that is (`nsChangeHint(0)`).
- #[inline]
- pub fn empty() -> Self {
- GeckoRestyleDamage(nsChangeHint(0))
- }
-
- /// Returns whether this restyle damage represents the empty damage.
- #[inline]
- pub fn is_empty(&self) -> bool {
- self.0 == nsChangeHint(0)
- }
-
- /// Computes the `StyleDifference` (including the appropriate change hint)
- /// given an old and a new style.
- pub fn compute_style_difference(
- old_style: &ComputedValues,
- new_style: &ComputedValues,
- ) -> StyleDifference {
- let mut any_style_changed = false;
- let mut reset_only = false;
- let hint = unsafe {
- bindings::Gecko_CalcStyleDifference(
- old_style.as_gecko_computed_style(),
- new_style.as_gecko_computed_style(),
- &mut any_style_changed,
- &mut reset_only,
- )
- };
- if reset_only && !old_style.custom_properties_equal(new_style) {
- // The Gecko_CalcStyleDifference call only checks the non-custom
- // property structs, so we check the custom properties here. Since
- // they generate no damage themselves, we can skip this check if we
- // already know we had some inherited (regular) property
- // differences.
- any_style_changed = true;
- reset_only = false;
- }
- let change = if any_style_changed {
- StyleChange::Changed { reset_only }
- } else {
- StyleChange::Unchanged
- };
- let damage = GeckoRestyleDamage(nsChangeHint(hint));
- StyleDifference { damage, change }
- }
-
- /// Returns true if this restyle damage contains all the damage of |other|.
- pub fn contains(self, other: Self) -> bool {
- self & other == other
- }
-
- /// Gets restyle damage to reconstruct the entire frame, subsuming all
- /// other damage.
- pub fn reconstruct() -> Self {
- GeckoRestyleDamage(structs::nsChangeHint::nsChangeHint_ReconstructFrame)
- }
-}
-
-impl Default for GeckoRestyleDamage {
- fn default() -> Self {
- Self::empty()
- }
-}
-
-impl BitOr for GeckoRestyleDamage {
- type Output = Self;
- fn bitor(self, other: Self) -> Self {
- GeckoRestyleDamage(self.0 | other.0)
- }
-}
-
-impl BitOrAssign for GeckoRestyleDamage {
- fn bitor_assign(&mut self, other: Self) {
- *self = *self | other;
- }
-}
-
-impl BitAnd for GeckoRestyleDamage {
- type Output = Self;
- fn bitand(self, other: Self) -> Self {
- GeckoRestyleDamage(nsChangeHint((self.0).0 & (other.0).0))
- }
-}
-
-impl Not for GeckoRestyleDamage {
- type Output = Self;
- fn not(self) -> Self {
- GeckoRestyleDamage(nsChangeHint(!(self.0).0))
- }
-}
diff --git a/components/style/gecko/selector_parser.rs b/components/style/gecko/selector_parser.rs
deleted file mode 100644
index 52549f9ecbe..00000000000
--- a/components/style/gecko/selector_parser.rs
+++ /dev/null
@@ -1,497 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
-
-//! Gecko-specific bits for selector-parsing.
-
-use crate::computed_value_flags::ComputedValueFlags;
-use crate::invalidation::element::document_state::InvalidationMatchingData;
-use crate::properties::ComputedValues;
-use crate::selector_parser::{Direction, HorizontalDirection, SelectorParser};
-use crate::str::starts_with_ignore_ascii_case;
-use crate::string_cache::{Atom, Namespace, WeakAtom, WeakNamespace};
-use crate::values::{AtomIdent, AtomString};
-use cssparser::{BasicParseError, BasicParseErrorKind, Parser};
-use cssparser::{CowRcStr, SourceLocation, ToCss, Token};
-use dom::{DocumentState, ElementState};
-use selectors::parser::SelectorParseErrorKind;
-use std::fmt;
-use style_traits::{CssWriter, ParseError, StyleParseErrorKind, ToCss as ToCss_};
-use thin_vec::ThinVec;
-
-pub use crate::gecko::pseudo_element::{
- PseudoElement, EAGER_PSEUDOS, EAGER_PSEUDO_COUNT, PSEUDO_COUNT,
-};
-pub use crate::gecko::snapshot::SnapshotMap;
-
-bitflags! {
- // See NonTSPseudoClass::is_enabled_in()
- struct NonTSPseudoClassFlag: u8 {
- const PSEUDO_CLASS_ENABLED_IN_UA_SHEETS = 1 << 0;
- const PSEUDO_CLASS_ENABLED_IN_CHROME = 1 << 1;
- const PSEUDO_CLASS_ENABLED_IN_UA_SHEETS_AND_CHROME =
- NonTSPseudoClassFlag::PSEUDO_CLASS_ENABLED_IN_UA_SHEETS.bits |
- NonTSPseudoClassFlag::PSEUDO_CLASS_ENABLED_IN_CHROME.bits;
- }
-}
-
-/// The type used to store the language argument to the `:lang` pseudo-class.
-#[derive(Clone, Debug, Eq, MallocSizeOf, PartialEq, ToCss, ToShmem)]
-#[css(comma)]
-pub struct Lang(#[css(iterable)] pub ThinVec<AtomIdent>);
-
-macro_rules! pseudo_class_name {
- ([$(($css:expr, $name:ident, $state:tt, $flags:tt),)*]) => {
- /// Our representation of a non tree-structural pseudo-class.
- #[derive(Clone, Debug, Eq, MallocSizeOf, PartialEq, ToShmem)]
- pub enum NonTSPseudoClass {
- $(
- #[doc = $css]
- $name,
- )*
- /// The `:lang` pseudo-class.
- Lang(Lang),
- /// The `:dir` pseudo-class.
- Dir(Direction),
- /// The non-standard `:-moz-locale-dir` pseudo-class.
- MozLocaleDir(Direction),
- }
- }
-}
-apply_non_ts_list!(pseudo_class_name);
-
-impl ToCss for NonTSPseudoClass {
- fn to_css<W>(&self, dest: &mut W) -> fmt::Result
- where
- W: fmt::Write,
- {
- macro_rules! pseudo_class_serialize {
- ([$(($css:expr, $name:ident, $state:tt, $flags:tt),)*]) => {
- match *self {
- $(NonTSPseudoClass::$name => concat!(":", $css),)*
- NonTSPseudoClass::Lang(ref lang) => {
- dest.write_str(":lang(")?;
- lang.to_css(&mut CssWriter::new(dest))?;
- return dest.write_char(')');
- },
- NonTSPseudoClass::MozLocaleDir(ref dir) => {
- dest.write_str(":-moz-locale-dir(")?;
- dir.to_css(&mut CssWriter::new(dest))?;
- return dest.write_char(')')
- },
- NonTSPseudoClass::Dir(ref dir) => {
- dest.write_str(":dir(")?;
- dir.to_css(&mut CssWriter::new(dest))?;
- return dest.write_char(')')
- },
- }
- }
- }
- let ser = apply_non_ts_list!(pseudo_class_serialize);
- dest.write_str(ser)
- }
-}
-
-impl NonTSPseudoClass {
- /// Parses the name and returns a non-ts-pseudo-class if succeeds.
- /// None otherwise. It doesn't check whether the pseudo-class is enabled
- /// in a particular state.
- pub fn parse_non_functional(name: &str) -> Option<Self> {
- macro_rules! pseudo_class_parse {
- ([$(($css:expr, $name:ident, $state:tt, $flags:tt),)*]) => {
- match_ignore_ascii_case! { &name,
- $($css => Some(NonTSPseudoClass::$name),)*
- "-moz-full-screen" => Some(NonTSPseudoClass::Fullscreen),
- "-moz-read-only" => Some(NonTSPseudoClass::ReadOnly),
- "-moz-read-write" => Some(NonTSPseudoClass::ReadWrite),
- "-moz-focusring" => Some(NonTSPseudoClass::FocusVisible),
- "-moz-ui-valid" => Some(NonTSPseudoClass::UserValid),
- "-moz-ui-invalid" => Some(NonTSPseudoClass::UserInvalid),
- "-webkit-autofill" => Some(NonTSPseudoClass::Autofill),
- _ => None,
- }
- }
- }
- apply_non_ts_list!(pseudo_class_parse)
- }
-
- /// Returns true if this pseudo-class has any of the given flags set.
- fn has_any_flag(&self, flags: NonTSPseudoClassFlag) -> bool {
- macro_rules! check_flag {
- (_) => {
- false
- };
- ($flags:ident) => {
- NonTSPseudoClassFlag::$flags.intersects(flags)
- };
- }
- macro_rules! pseudo_class_check_is_enabled_in {
- ([$(($css:expr, $name:ident, $state:tt, $flags:tt),)*]) => {
- match *self {
- $(NonTSPseudoClass::$name => check_flag!($flags),)*
- NonTSPseudoClass::MozLocaleDir(_) => check_flag!(PSEUDO_CLASS_ENABLED_IN_UA_SHEETS_AND_CHROME),
- NonTSPseudoClass::Lang(_) |
- NonTSPseudoClass::Dir(_) => false,
- }
- }
- }
- apply_non_ts_list!(pseudo_class_check_is_enabled_in)
- }
-
- /// Returns whether the pseudo-class is enabled in content sheets.
- #[inline]
- fn is_enabled_in_content(&self) -> bool {
- if matches!(*self, Self::PopoverOpen) {
- return static_prefs::pref!("dom.element.popover.enabled");
- }
- !self.has_any_flag(NonTSPseudoClassFlag::PSEUDO_CLASS_ENABLED_IN_UA_SHEETS_AND_CHROME)
- }
-
- /// Get the state flag associated with a pseudo-class, if any.
- pub fn state_flag(&self) -> ElementState {
- macro_rules! flag {
- (_) => {
- ElementState::empty()
- };
- ($state:ident) => {
- ElementState::$state
- };
- }
- macro_rules! pseudo_class_state {
- ([$(($css:expr, $name:ident, $state:tt, $flags:tt),)*]) => {
- match *self {
- $(NonTSPseudoClass::$name => flag!($state),)*
- NonTSPseudoClass::Dir(ref dir) => dir.element_state(),
- NonTSPseudoClass::MozLocaleDir(..) |
- NonTSPseudoClass::Lang(..) => ElementState::empty(),
- }
- }
- }
- apply_non_ts_list!(pseudo_class_state)
- }
-
- /// Get the document state flag associated with a pseudo-class, if any.
- pub fn document_state_flag(&self) -> DocumentState {
- match *self {
- NonTSPseudoClass::MozLocaleDir(ref dir) => match dir.as_horizontal_direction() {
- Some(HorizontalDirection::Ltr) => DocumentState::LTR_LOCALE,
- Some(HorizontalDirection::Rtl) => DocumentState::RTL_LOCALE,
- None => DocumentState::empty(),
- },
- NonTSPseudoClass::MozWindowInactive => DocumentState::WINDOW_INACTIVE,
- NonTSPseudoClass::MozLWTheme => DocumentState::LWTHEME,
- _ => DocumentState::empty(),
- }
- }
-
- /// Returns true if the given pseudoclass should trigger style sharing cache
- /// revalidation.
- pub fn needs_cache_revalidation(&self) -> bool {
- self.state_flag().is_empty() &&
- !matches!(
- *self,
- // :dir() depends on state only, but may have an empty
- // state_flag for invalid arguments.
- NonTSPseudoClass::Dir(_) |
- // :-moz-is-html only depends on the state of the document and
- // the namespace of the element; the former is invariant
- // across all the elements involved and the latter is already
- // checked for by our caching precondtions.
- NonTSPseudoClass::MozIsHTML |
- // We prevent style sharing for NAC.
- NonTSPseudoClass::MozNativeAnonymous |
- // :-moz-placeholder is parsed but never matches.
- NonTSPseudoClass::MozPlaceholder |
- // :-moz-lwtheme, :-moz-locale-dir and
- // :-moz-window-inactive depend only on the state of the
- // document, which is invariant across all the elements
- // involved in a given style cache.
- NonTSPseudoClass::MozLWTheme |
- NonTSPseudoClass::MozLocaleDir(_) |
- NonTSPseudoClass::MozWindowInactive
- )
- }
-}
-
-impl ::selectors::parser::NonTSPseudoClass for NonTSPseudoClass {
- type Impl = SelectorImpl;
-
- #[inline]
- fn is_active_or_hover(&self) -> bool {
- matches!(*self, NonTSPseudoClass::Active | NonTSPseudoClass::Hover)
- }
-
- /// We intentionally skip the link-related ones.
- #[inline]
- fn is_user_action_state(&self) -> bool {
- matches!(
- *self,
- NonTSPseudoClass::Hover | NonTSPseudoClass::Active | NonTSPseudoClass::Focus
- )
- }
-}
-
-/// The dummy struct we use to implement our selector parsing.
-#[derive(Clone, Debug, Eq, PartialEq)]
-pub struct SelectorImpl;
-
-/// A set of extra data to carry along with the matching context, either for
-/// selector-matching or invalidation.
-#[derive(Default)]
-pub struct ExtraMatchingData<'a> {
- /// The invalidation data to invalidate doc-state pseudo-classes correctly.
- pub invalidation_data: InvalidationMatchingData,
-
- /// The invalidation bits from matching container queries. These are here
- /// just for convenience mostly.
- pub cascade_input_flags: ComputedValueFlags,
-
- /// The style of the originating element in order to evaluate @container
- /// size queries affecting pseudo-elements.
- pub originating_element_style: Option<&'a ComputedValues>,
-}
-
-impl ::selectors::SelectorImpl for SelectorImpl {
- type ExtraMatchingData<'a> = ExtraMatchingData<'a>;
- type AttrValue = AtomString;
- type Identifier = AtomIdent;
- type LocalName = AtomIdent;
- type NamespacePrefix = AtomIdent;
- type NamespaceUrl = Namespace;
- type BorrowedNamespaceUrl = WeakNamespace;
- type BorrowedLocalName = WeakAtom;
-
- type PseudoElement = PseudoElement;
- type NonTSPseudoClass = NonTSPseudoClass;
-
- fn should_collect_attr_hash(name: &AtomIdent) -> bool {
- !crate::bloom::is_attr_name_excluded_from_filter(name)
- }
-}
-
-impl<'a> SelectorParser<'a> {
- fn is_pseudo_class_enabled(&self, pseudo_class: &NonTSPseudoClass) -> bool {
- if pseudo_class.is_enabled_in_content() {
- return true;
- }
-
- if self.in_user_agent_stylesheet() &&
- pseudo_class.has_any_flag(NonTSPseudoClassFlag::PSEUDO_CLASS_ENABLED_IN_UA_SHEETS)
- {
- return true;
- }
-
- if self.chrome_rules_enabled() &&
- pseudo_class.has_any_flag(NonTSPseudoClassFlag::PSEUDO_CLASS_ENABLED_IN_CHROME)
- {
- return true;
- }
-
- return false;
- }
-
- fn is_pseudo_element_enabled(&self, pseudo_element: &PseudoElement) -> bool {
- if pseudo_element.enabled_in_content() {
- return true;
- }
-
- if self.in_user_agent_stylesheet() && pseudo_element.enabled_in_ua_sheets() {
- return true;
- }
-
- if self.chrome_rules_enabled() && pseudo_element.enabled_in_chrome() {
- return true;
- }
-
- return false;
- }
-}
-
-impl<'a, 'i> ::selectors::Parser<'i> for SelectorParser<'a> {
- type Impl = SelectorImpl;
- type Error = StyleParseErrorKind<'i>;
-
- fn parse_parent_selector(&self) -> bool {
- static_prefs::pref!("layout.css.nesting.enabled")
- }
-
- #[inline]
- fn parse_slotted(&self) -> bool {
- true
- }
-
- #[inline]
- fn parse_host(&self) -> bool {
- true
- }
-
- #[inline]
- fn parse_nth_child_of(&self) -> bool {
- true
- }
-
- #[inline]
- fn parse_is_and_where(&self) -> bool {
- true
- }
-
- #[inline]
- fn parse_has(&self) -> bool {
- static_prefs::pref!("layout.css.has-selector.enabled")
- }
-
- #[inline]
- fn parse_part(&self) -> bool {
- true
- }
-
- #[inline]
- fn is_is_alias(&self, function: &str) -> bool {
- function.eq_ignore_ascii_case("-moz-any")
- }
-
- #[inline]
- fn allow_forgiving_selectors(&self) -> bool {
- !self.for_supports_rule
- }
-
- fn parse_non_ts_pseudo_class(
- &self,
- location: SourceLocation,
- name: CowRcStr<'i>,
- ) -> Result<NonTSPseudoClass, ParseError<'i>> {
- if let Some(pseudo_class) = NonTSPseudoClass::parse_non_functional(&name) {
- if self.is_pseudo_class_enabled(&pseudo_class) {
- return Ok(pseudo_class);
- }
- }
- Err(
- location.new_custom_error(SelectorParseErrorKind::UnsupportedPseudoClassOrElement(
- name,
- )),
- )
- }
-
- fn parse_non_ts_functional_pseudo_class<'t>(
- &self,
- name: CowRcStr<'i>,
- parser: &mut Parser<'i, 't>,
- ) -> Result<NonTSPseudoClass, ParseError<'i>> {
- let pseudo_class = match_ignore_ascii_case! { &name,
- "lang" => {
- let result = parser.parse_comma_separated(|input| {
- Ok(AtomIdent::from(input.expect_ident_or_string()?.as_ref()))
- })?;
- if result.is_empty() {
- return Err(parser.new_custom_error(StyleParseErrorKind::UnspecifiedError));
- }
- NonTSPseudoClass::Lang(Lang(result.into()))
- },
- "-moz-locale-dir" => {
- NonTSPseudoClass::MozLocaleDir(Direction::parse(parser)?)
- },
- "dir" => {
- NonTSPseudoClass::Dir(Direction::parse(parser)?)
- },
- _ => return Err(parser.new_custom_error(
- SelectorParseErrorKind::UnsupportedPseudoClassOrElement(name.clone())
- ))
- };
- if self.is_pseudo_class_enabled(&pseudo_class) {
- Ok(pseudo_class)
- } else {
- Err(
- parser.new_custom_error(SelectorParseErrorKind::UnsupportedPseudoClassOrElement(
- name,
- )),
- )
- }
- }
-
- fn parse_pseudo_element(
- &self,
- location: SourceLocation,
- name: CowRcStr<'i>,
- ) -> Result<PseudoElement, ParseError<'i>> {
- let allow_unkown_webkit = !self.for_supports_rule;
- if let Some(pseudo) = PseudoElement::from_slice(&name, allow_unkown_webkit) {
- if self.is_pseudo_element_enabled(&pseudo) {
- return Ok(pseudo);
- }
- }
-
- Err(
- location.new_custom_error(SelectorParseErrorKind::UnsupportedPseudoClassOrElement(
- name,
- )),
- )
- }
-
- fn parse_functional_pseudo_element<'t>(
- &self,
- name: CowRcStr<'i>,
- parser: &mut Parser<'i, 't>,
- ) -> Result<PseudoElement, ParseError<'i>> {
- if starts_with_ignore_ascii_case(&name, "-moz-tree-") {
- // Tree pseudo-elements can have zero or more arguments, separated
- // by either comma or space.
- let mut args = ThinVec::new();
- loop {
- let location = parser.current_source_location();
- match parser.next() {
- Ok(&Token::Ident(ref ident)) => args.push(Atom::from(ident.as_ref())),
- Ok(&Token::Comma) => {},
- Ok(t) => return Err(location.new_unexpected_token_error(t.clone())),
- Err(BasicParseError {
- kind: BasicParseErrorKind::EndOfInput,
- ..
- }) => break,
- _ => unreachable!("Parser::next() shouldn't return any other error"),
- }
- }
- if let Some(pseudo) = PseudoElement::tree_pseudo_element(&name, args) {
- if self.is_pseudo_element_enabled(&pseudo) {
- return Ok(pseudo);
- }
- }
- } else if name.eq_ignore_ascii_case("highlight") {
- let pseudo = PseudoElement::Highlight(AtomIdent::from(parser.expect_ident()?.as_ref()));
- if self.is_pseudo_element_enabled(&pseudo) {
- return Ok(pseudo);
- }
- }
- Err(
- parser.new_custom_error(SelectorParseErrorKind::UnsupportedPseudoClassOrElement(
- name,
- )),
- )
- }
-
- fn default_namespace(&self) -> Option<Namespace> {
- self.namespaces.default.clone()
- }
-
- fn namespace_for_prefix(&self, prefix: &AtomIdent) -> Option<Namespace> {
- self.namespaces.prefixes.get(prefix).cloned()
- }
-}
-
-impl SelectorImpl {
- /// A helper to traverse each eagerly cascaded pseudo-element, executing
- /// `fun` on it.
- #[inline]
- pub fn each_eagerly_cascaded_pseudo_element<F>(mut fun: F)
- where
- F: FnMut(PseudoElement),
- {
- for pseudo in &EAGER_PSEUDOS {
- fun(pseudo.clone())
- }
- }
-}
-
-// Selector and component sizes are important for matching performance.
-size_of_test!(selectors::parser::Selector<SelectorImpl>, 8);
-size_of_test!(selectors::parser::Component<SelectorImpl>, 24);
-size_of_test!(PseudoElement, 16);
-size_of_test!(NonTSPseudoClass, 16);
diff --git a/components/style/gecko/snapshot.rs b/components/style/gecko/snapshot.rs
deleted file mode 100644
index 372e7fdb7f5..00000000000
--- a/components/style/gecko/snapshot.rs
+++ /dev/null
@@ -1,174 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
-
-//! A gecko snapshot, that stores the element attributes and state before they
-//! change in order to properly calculate restyle hints.
-
-use crate::dom::TElement;
-use crate::gecko::snapshot_helpers;
-use crate::gecko::wrapper::GeckoElement;
-use crate::gecko_bindings::bindings;
-use crate::gecko_bindings::structs::ServoElementSnapshot;
-use crate::gecko_bindings::structs::ServoElementSnapshotFlags as Flags;
-use crate::gecko_bindings::structs::ServoElementSnapshotTable;
-use crate::invalidation::element::element_wrapper::ElementSnapshot;
-use crate::selector_parser::AttrValue;
-use crate::string_cache::{Atom, Namespace};
-use crate::values::{AtomIdent, AtomString};
-use crate::LocalName;
-use crate::WeakAtom;
-use dom::ElementState;
-use selectors::attr::{AttrSelectorOperation, CaseSensitivity, NamespaceConstraint};
-
-/// A snapshot of a Gecko element.
-pub type GeckoElementSnapshot = ServoElementSnapshot;
-
-/// A map from elements to snapshots for Gecko's style back-end.
-pub type SnapshotMap = ServoElementSnapshotTable;
-
-impl SnapshotMap {
- /// Gets the snapshot for this element, if any.
- ///
- /// FIXME(emilio): The transmute() business we do here is kind of nasty, but
- /// it's a consequence of the map being a OpaqueNode -> Snapshot table in
- /// Servo and an Element -> Snapshot table in Gecko.
- ///
- /// We should be able to make this a more type-safe with type annotations by
- /// making SnapshotMap a trait and moving the implementations outside, but
- /// that's a pain because it implies parameterizing SharedStyleContext.
- pub fn get<E: TElement>(&self, element: &E) -> Option<&GeckoElementSnapshot> {
- debug_assert!(element.has_snapshot());
-
- unsafe {
- let element = ::std::mem::transmute::<&E, &GeckoElement>(element);
- bindings::Gecko_GetElementSnapshot(self, element.0).as_ref()
- }
- }
-}
-
-impl GeckoElementSnapshot {
- #[inline]
- fn has_any(&self, flags: Flags) -> bool {
- (self.mContains as u8 & flags as u8) != 0
- }
-
- /// Returns true if the snapshot has stored state for pseudo-classes
- /// that depend on things other than `ElementState`.
- #[inline]
- pub fn has_other_pseudo_class_state(&self) -> bool {
- self.has_any(Flags::OtherPseudoClassState)
- }
-
- /// Returns true if the snapshot recorded an id change.
- #[inline]
- pub fn id_changed(&self) -> bool {
- self.mIdAttributeChanged()
- }
-
- /// Returns true if the snapshot recorded a class attribute change.
- #[inline]
- pub fn class_changed(&self) -> bool {
- self.mClassAttributeChanged()
- }
-
- /// Executes the callback once for each attribute that changed.
- #[inline]
- pub fn each_attr_changed<F>(&self, mut callback: F)
- where
- F: FnMut(&AtomIdent),
- {
- for attr in self.mChangedAttrNames.iter() {
- unsafe { AtomIdent::with(attr.mRawPtr, &mut callback) }
- }
- }
-
- /// selectors::Element::attr_matches
- pub fn attr_matches(
- &self,
- ns: &NamespaceConstraint<&Namespace>,
- local_name: &LocalName,
- operation: &AttrSelectorOperation<&AttrValue>,
- ) -> bool {
- snapshot_helpers::attr_matches(self.mAttrs.iter(), ns, local_name, operation)
- }
-}
-
-impl ElementSnapshot for GeckoElementSnapshot {
- fn debug_list_attributes(&self) -> String {
- use nsstring::nsCString;
- let mut string = nsCString::new();
- unsafe {
- bindings::Gecko_Snapshot_DebugListAttributes(self, &mut string);
- }
- String::from_utf8_lossy(&*string).into_owned()
- }
-
- fn state(&self) -> Option<ElementState> {
- if self.has_any(Flags::State) {
- Some(ElementState::from_bits_truncate(self.mState))
- } else {
- None
- }
- }
-
- #[inline]
- fn has_attrs(&self) -> bool {
- self.has_any(Flags::Attributes)
- }
-
- #[inline]
- fn id_attr(&self) -> Option<&WeakAtom> {
- if !self.has_any(Flags::Id) {
- return None;
- }
-
- snapshot_helpers::get_id(&*self.mAttrs)
- }
-
- #[inline]
- fn is_part(&self, name: &AtomIdent) -> bool {
- let attr = match snapshot_helpers::find_attr(&*self.mAttrs, &atom!("part")) {
- Some(attr) => attr,
- None => return false,
- };
-
- snapshot_helpers::has_class_or_part(name, CaseSensitivity::CaseSensitive, attr)
- }
-
- #[inline]
- fn imported_part(&self, name: &AtomIdent) -> Option<AtomIdent> {
- snapshot_helpers::imported_part(&*self.mAttrs, name)
- }
-
- #[inline]
- fn has_class(&self, name: &AtomIdent, case_sensitivity: CaseSensitivity) -> bool {
- if !self.has_any(Flags::MaybeClass) {
- return false;
- }
-
- snapshot_helpers::has_class_or_part(name, case_sensitivity, &self.mClass)
- }
-
- #[inline]
- fn each_class<F>(&self, callback: F)
- where
- F: FnMut(&AtomIdent),
- {
- if !self.has_any(Flags::MaybeClass) {
- return;
- }
-
- snapshot_helpers::each_class_or_part(&self.mClass, callback)
- }
-
- #[inline]
- fn lang_attr(&self) -> Option<AtomString> {
- let ptr = unsafe { bindings::Gecko_SnapshotLangValue(self) };
- if ptr.is_null() {
- None
- } else {
- Some(AtomString(unsafe { Atom::from_addrefed(ptr) }))
- }
- }
-}
diff --git a/components/style/gecko/snapshot_helpers.rs b/components/style/gecko/snapshot_helpers.rs
deleted file mode 100644
index 818e767e0e9..00000000000
--- a/components/style/gecko/snapshot_helpers.rs
+++ /dev/null
@@ -1,309 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
-
-//! Element an snapshot common logic.
-
-use crate::dom::TElement;
-use crate::gecko::wrapper::namespace_id_to_atom;
-use crate::gecko_bindings::bindings;
-use crate::gecko_bindings::structs::{self, nsAtom};
-use crate::invalidation::element::element_wrapper::ElementSnapshot;
-use crate::selector_parser::{AttrValue, SnapshotMap};
-use crate::string_cache::WeakAtom;
-use crate::values::AtomIdent;
-use crate::{Atom, CaseSensitivityExt, LocalName, Namespace};
-use selectors::attr::{CaseSensitivity, NamespaceConstraint, AttrSelectorOperation, AttrSelectorOperator};
-use smallvec::SmallVec;
-
-/// A function that, given an element of type `T`, allows you to get a single
-/// class or a class list.
-enum Class<'a> {
- None,
- One(*const nsAtom),
- More(&'a [structs::RefPtr<nsAtom>]),
-}
-
-#[inline(always)]
-fn base_type(attr: &structs::nsAttrValue) -> structs::nsAttrValue_ValueBaseType {
- (attr.mBits & structs::NS_ATTRVALUE_BASETYPE_MASK) as structs::nsAttrValue_ValueBaseType
-}
-
-#[inline(always)]
-unsafe fn ptr<T>(attr: &structs::nsAttrValue) -> *const T {
- (attr.mBits & !structs::NS_ATTRVALUE_BASETYPE_MASK) as *const T
-}
-
-#[inline(always)]
-unsafe fn get_class_or_part_from_attr(attr: &structs::nsAttrValue) -> Class {
- debug_assert!(bindings::Gecko_AssertClassAttrValueIsSane(attr));
- let base_type = base_type(attr);
- if base_type == structs::nsAttrValue_ValueBaseType_eAtomBase {
- return Class::One(ptr::<nsAtom>(attr));
- }
- if base_type == structs::nsAttrValue_ValueBaseType_eOtherBase {
- let container = ptr::<structs::MiscContainer>(attr);
- debug_assert_eq!(
- (*container).mType,
- structs::nsAttrValue_ValueType_eAtomArray
- );
- // NOTE: Bindgen doesn't deal with AutoTArray, so cast it below.
- let attr_array: *mut _ = *(*container)
- .__bindgen_anon_1
- .mValue
- .as_ref()
- .__bindgen_anon_1
- .mAtomArray
- .as_ref();
- let array =
- (*attr_array).mArray.as_ptr() as *const structs::nsTArray<structs::RefPtr<nsAtom>>;
- return Class::More(&**array);
- }
- debug_assert_eq!(base_type, structs::nsAttrValue_ValueBaseType_eStringBase);
- Class::None
-}
-
-#[inline(always)]
-unsafe fn get_id_from_attr(attr: &structs::nsAttrValue) -> &WeakAtom {
- debug_assert_eq!(
- base_type(attr),
- structs::nsAttrValue_ValueBaseType_eAtomBase
- );
- WeakAtom::new(ptr::<nsAtom>(attr))
-}
-
-impl structs::nsAttrName {
- #[inline]
- fn is_nodeinfo(&self) -> bool {
- self.mBits & 1 != 0
- }
-
- #[inline]
- unsafe fn as_nodeinfo(&self) -> &structs::NodeInfo {
- debug_assert!(self.is_nodeinfo());
- &*((self.mBits & !1) as *const structs::NodeInfo)
- }
-
- #[inline]
- fn namespace_id(&self) -> i32 {
- if !self.is_nodeinfo() {
- return structs::kNameSpaceID_None;
- }
- unsafe { self.as_nodeinfo() }.mInner.mNamespaceID
- }
-
- /// Returns the attribute name as an atom pointer.
- #[inline]
- pub fn name(&self) -> *const nsAtom {
- if self.is_nodeinfo() {
- unsafe { self.as_nodeinfo() }.mInner.mName
- } else {
- self.mBits as *const nsAtom
- }
- }
-}
-
-/// Find an attribute value with a given name and no namespace.
-#[inline(always)]
-pub fn find_attr<'a>(
- attrs: &'a [structs::AttrArray_InternalAttr],
- name: &Atom,
-) -> Option<&'a structs::nsAttrValue> {
- attrs
- .iter()
- .find(|attr| attr.mName.mBits == name.as_ptr() as usize)
- .map(|attr| &attr.mValue)
-}
-
-/// Finds the id attribute from a list of attributes.
-#[inline(always)]
-pub fn get_id(attrs: &[structs::AttrArray_InternalAttr]) -> Option<&WeakAtom> {
- Some(unsafe { get_id_from_attr(find_attr(attrs, &atom!("id"))?) })
-}
-
-#[inline(always)]
-pub(super) fn each_exported_part(
- attrs: &[structs::AttrArray_InternalAttr],
- name: &AtomIdent,
- mut callback: impl FnMut(&AtomIdent),
-) {
- let attr = match find_attr(attrs, &atom!("exportparts")) {
- Some(attr) => attr,
- None => return,
- };
- let mut length = 0;
- let atoms = unsafe { bindings::Gecko_Element_ExportedParts(attr, name.as_ptr(), &mut length) };
- if atoms.is_null() {
- return;
- }
-
- unsafe {
- for atom in std::slice::from_raw_parts(atoms, length) {
- AtomIdent::with(*atom, &mut callback)
- }
- }
-}
-
-#[inline(always)]
-pub(super) fn imported_part(
- attrs: &[structs::AttrArray_InternalAttr],
- name: &AtomIdent,
-) -> Option<AtomIdent> {
- let attr = find_attr(attrs, &atom!("exportparts"))?;
- let atom = unsafe { bindings::Gecko_Element_ImportedPart(attr, name.as_ptr()) };
- if atom.is_null() {
- return None;
- }
- Some(AtomIdent(unsafe { Atom::from_raw(atom) }))
-}
-
-/// Given a class or part name, a case sensitivity, and an array of attributes,
-/// returns whether the attribute has that name.
-#[inline(always)]
-pub fn has_class_or_part(
- name: &AtomIdent,
- case_sensitivity: CaseSensitivity,
- attr: &structs::nsAttrValue,
-) -> bool {
- match unsafe { get_class_or_part_from_attr(attr) } {
- Class::None => false,
- Class::One(atom) => unsafe { case_sensitivity.eq_atom(name, WeakAtom::new(atom)) },
- Class::More(atoms) => match case_sensitivity {
- CaseSensitivity::CaseSensitive => {
- let name_ptr = name.as_ptr();
- atoms.iter().any(|atom| atom.mRawPtr == name_ptr)
- },
- CaseSensitivity::AsciiCaseInsensitive => unsafe {
- atoms
- .iter()
- .any(|atom| WeakAtom::new(atom.mRawPtr).eq_ignore_ascii_case(name))
- },
- },
- }
-}
-
-/// Given an item, a callback, and a getter, execute `callback` for each class
-/// or part name this `item` has.
-#[inline(always)]
-pub fn each_class_or_part<F>(attr: &structs::nsAttrValue, mut callback: F)
-where
- F: FnMut(&AtomIdent),
-{
- unsafe {
- match get_class_or_part_from_attr(attr) {
- Class::None => {},
- Class::One(atom) => AtomIdent::with(atom, callback),
- Class::More(atoms) => {
- for atom in atoms {
- AtomIdent::with(atom.mRawPtr, &mut callback)
- }
- },
- }
- }
-}
-
-/// Returns a list of classes that were either added to or removed from the
-/// element since the snapshot.
-pub fn classes_changed<E: TElement>(element: &E, snapshots: &SnapshotMap) -> SmallVec<[Atom; 8]> {
- debug_assert!(element.has_snapshot(), "Why bothering?");
- let snapshot = snapshots.get(element).expect("has_snapshot lied");
- if !snapshot.class_changed() {
- return SmallVec::new();
- }
-
- let mut classes_changed = SmallVec::<[Atom; 8]>::new();
- snapshot.each_class(|c| {
- if !element.has_class(c, CaseSensitivity::CaseSensitive) {
- classes_changed.push(c.0.clone());
- }
- });
- element.each_class(|c| {
- if !snapshot.has_class(c, CaseSensitivity::CaseSensitive) {
- classes_changed.push(c.0.clone());
- }
- });
-
- classes_changed
-}
-
-/// Returns whether a given attribute selector matches given the internal attrs.
-pub(crate) fn attr_matches<'a>(
- iter: impl Iterator<Item = &'a structs::AttrArray_InternalAttr>,
- ns: &NamespaceConstraint<&Namespace>,
- local_name: &LocalName,
- operation: &AttrSelectorOperation<&AttrValue>,
-) -> bool {
- let name_ptr = local_name.as_ptr();
- for attr in iter {
- if attr.mName.name() != name_ptr {
- continue;
- }
-
- let ns_matches = match *ns {
- NamespaceConstraint::Any => true,
- NamespaceConstraint::Specific(ns) => {
- if *ns == ns!() {
- !attr.mName.is_nodeinfo()
- } else {
- ns.as_ptr() == unsafe { namespace_id_to_atom(attr.mName.namespace_id()) }
- }
- },
- };
-
- if !ns_matches {
- continue;
- }
-
- let (operator, case_sensitivity, value) = match *operation {
- AttrSelectorOperation::Exists => return true,
- AttrSelectorOperation::WithValue {
- operator,
- case_sensitivity,
- value,
- } => (operator, case_sensitivity, value),
- };
- let ignore_case = match case_sensitivity {
- CaseSensitivity::CaseSensitive => false,
- CaseSensitivity::AsciiCaseInsensitive => true,
- };
- let value = value.as_ptr();
- let matches = unsafe {
- match operator {
- AttrSelectorOperator::Equal => bindings::Gecko_AttrEquals(
- &attr.mValue,
- value,
- ignore_case,
- ),
- AttrSelectorOperator::Includes => bindings::Gecko_AttrIncludes(
- &attr.mValue,
- value,
- ignore_case,
- ),
- AttrSelectorOperator::DashMatch => bindings::Gecko_AttrDashEquals(
- &attr.mValue,
- value,
- ignore_case,
- ),
- AttrSelectorOperator::Prefix => bindings::Gecko_AttrHasPrefix(
- &attr.mValue,
- value,
- ignore_case,
- ),
- AttrSelectorOperator::Suffix => bindings::Gecko_AttrHasSuffix(
- &attr.mValue,
- value,
- ignore_case,
- ),
- AttrSelectorOperator::Substring => bindings::Gecko_AttrHasSubstring(
- &attr.mValue,
- value,
- ignore_case,
- ),
- }
- };
- if matches || *ns != NamespaceConstraint::Any {
- return matches;
- }
- }
- false
-}
diff --git a/components/style/gecko/traversal.rs b/components/style/gecko/traversal.rs
deleted file mode 100644
index 71d1a2f949b..00000000000
--- a/components/style/gecko/traversal.rs
+++ /dev/null
@@ -1,53 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
-
-//! Gecko-specific bits for the styling DOM traversal.
-
-use crate::context::{SharedStyleContext, StyleContext};
-use crate::dom::{TElement, TNode};
-use crate::gecko::wrapper::{GeckoElement, GeckoNode};
-use crate::traversal::{recalc_style_at, DomTraversal, PerLevelTraversalData};
-
-/// This is the simple struct that Gecko uses to encapsulate a DOM traversal for
-/// styling.
-pub struct RecalcStyleOnly<'a> {
- shared: SharedStyleContext<'a>,
-}
-
-impl<'a> RecalcStyleOnly<'a> {
- /// Create a `RecalcStyleOnly` traversal from a `SharedStyleContext`.
- pub fn new(shared: SharedStyleContext<'a>) -> Self {
- RecalcStyleOnly { shared: shared }
- }
-}
-
-impl<'recalc, 'le> DomTraversal<GeckoElement<'le>> for RecalcStyleOnly<'recalc> {
- fn process_preorder<F>(
- &self,
- traversal_data: &PerLevelTraversalData,
- context: &mut StyleContext<GeckoElement<'le>>,
- node: GeckoNode<'le>,
- note_child: F,
- ) where
- F: FnMut(GeckoNode<'le>),
- {
- if let Some(el) = node.as_element() {
- let mut data = unsafe { el.ensure_data() };
- recalc_style_at(self, traversal_data, context, el, &mut data, note_child);
- }
- }
-
- fn process_postorder(&self, _: &mut StyleContext<GeckoElement<'le>>, _: GeckoNode<'le>) {
- unreachable!();
- }
-
- /// We don't use the post-order traversal for anything.
- fn needs_postorder_traversal() -> bool {
- false
- }
-
- fn shared_context(&self) -> &SharedStyleContext {
- &self.shared
- }
-}
diff --git a/components/style/gecko/url.rs b/components/style/gecko/url.rs
deleted file mode 100644
index 8cf4aa51c0a..00000000000
--- a/components/style/gecko/url.rs
+++ /dev/null
@@ -1,383 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
-
-//! Common handling for the specified value CSS url() values.
-
-use crate::gecko_bindings::bindings;
-use crate::gecko_bindings::structs;
-use crate::parser::{Parse, ParserContext};
-use crate::stylesheets::{CorsMode, UrlExtraData};
-use crate::values::computed::{Context, ToComputedValue};
-use cssparser::Parser;
-use malloc_size_of::{MallocSizeOf, MallocSizeOfOps};
-use nsstring::nsCString;
-use servo_arc::Arc;
-use std::collections::HashMap;
-use std::fmt::{self, Write};
-use std::mem::ManuallyDrop;
-use std::sync::RwLock;
-use style_traits::{CssWriter, ParseError, ToCss};
-use to_shmem::{self, SharedMemoryBuilder, ToShmem};
-
-/// A CSS url() value for gecko.
-#[derive(Clone, Debug, PartialEq, SpecifiedValueInfo, ToCss, ToShmem)]
-#[css(function = "url")]
-#[repr(C)]
-pub struct CssUrl(pub Arc<CssUrlData>);
-
-/// Data shared between CssUrls.
-///
-/// cbindgen:derive-eq=false
-/// cbindgen:derive-neq=false
-#[derive(Debug, SpecifiedValueInfo, ToCss, ToShmem)]
-#[repr(C)]
-pub struct CssUrlData {
- /// The URL in unresolved string form.
- serialization: crate::OwnedStr,
-
- /// The URL extra data.
- #[css(skip)]
- pub extra_data: UrlExtraData,
-
- /// The CORS mode that will be used for the load.
- #[css(skip)]
- cors_mode: CorsMode,
-
- /// Data to trigger a load from Gecko. This is mutable in C++.
- ///
- /// TODO(emilio): Maybe we can eagerly resolve URLs and make this immutable?
- #[css(skip)]
- load_data: LoadDataSource,
-}
-
-impl PartialEq for CssUrlData {
- fn eq(&self, other: &Self) -> bool {
- self.serialization == other.serialization &&
- self.extra_data == other.extra_data &&
- self.cors_mode == other.cors_mode
- }
-}
-
-impl CssUrl {
- fn parse_with_cors_mode<'i, 't>(
- context: &ParserContext,
- input: &mut Parser<'i, 't>,
- cors_mode: CorsMode,
- ) -> Result<Self, ParseError<'i>> {
- let url = input.expect_url()?;
- Ok(Self::parse_from_string(
- url.as_ref().to_owned(),
- context,
- cors_mode,
- ))
- }
-
- /// Parse a URL from a string value that is a valid CSS token for a URL.
- pub fn parse_from_string(url: String, context: &ParserContext, cors_mode: CorsMode) -> Self {
- CssUrl(Arc::new(CssUrlData {
- serialization: url.into(),
- extra_data: context.url_data.clone(),
- cors_mode,
- load_data: LoadDataSource::Owned(LoadData::default()),
- }))
- }
-
- /// Returns true if the URL is definitely invalid. We don't eagerly resolve
- /// URLs in gecko, so we just return false here.
- /// use its |resolved| status.
- pub fn is_invalid(&self) -> bool {
- false
- }
-
- /// Returns true if this URL looks like a fragment.
- /// See https://drafts.csswg.org/css-values/#local-urls
- #[inline]
- pub fn is_fragment(&self) -> bool {
- self.0.is_fragment()
- }
-
- /// Return the unresolved url as string, or the empty string if it's
- /// invalid.
- #[inline]
- pub fn as_str(&self) -> &str {
- self.0.as_str()
- }
-}
-
-impl CssUrlData {
- /// Returns true if this URL looks like a fragment.
- /// See https://drafts.csswg.org/css-values/#local-urls
- pub fn is_fragment(&self) -> bool {
- self.as_str()
- .as_bytes()
- .iter()
- .next()
- .map_or(false, |b| *b == b'#')
- }
-
- /// Return the unresolved url as string, or the empty string if it's
- /// invalid.
- pub fn as_str(&self) -> &str {
- &*self.serialization
- }
-}
-
-impl Parse for CssUrl {
- fn parse<'i, 't>(
- context: &ParserContext,
- input: &mut Parser<'i, 't>,
- ) -> Result<Self, ParseError<'i>> {
- Self::parse_with_cors_mode(context, input, CorsMode::None)
- }
-}
-
-impl Eq for CssUrl {}
-
-impl MallocSizeOf for CssUrl {
- fn size_of(&self, _ops: &mut MallocSizeOfOps) -> usize {
- // XXX: measure `serialization` once bug 1397971 lands
-
- // We ignore `extra_data`, because RefPtr is tricky, and there aren't
- // many of them in practise (sharing is common).
-
- 0
- }
-}
-
-/// A key type for LOAD_DATA_TABLE.
-#[derive(Eq, Hash, PartialEq)]
-struct LoadDataKey(*const LoadDataSource);
-
-unsafe impl Sync for LoadDataKey {}
-unsafe impl Send for LoadDataKey {}
-
-bitflags! {
- /// Various bits of mutable state that are kept for image loads.
- #[repr(C)]
- pub struct LoadDataFlags: u8 {
- /// Whether we tried to resolve the uri at least once.
- const TRIED_TO_RESOLVE_URI = 1 << 0;
- /// Whether we tried to resolve the image at least once.
- const TRIED_TO_RESOLVE_IMAGE = 1 << 1;
- }
-}
-
-/// This is usable and movable from multiple threads just fine, as long as it's
-/// not cloned (it is not clonable), and the methods that mutate it run only on
-/// the main thread (when all the other threads we care about are paused).
-unsafe impl Sync for LoadData {}
-unsafe impl Send for LoadData {}
-
-/// The load data for a given URL. This is mutable from C++, and shouldn't be
-/// accessed from rust for anything.
-#[repr(C)]
-#[derive(Debug)]
-pub struct LoadData {
- /// A strong reference to the imgRequestProxy, if any, that should be
- /// released on drop.
- ///
- /// These are raw pointers because they are not safe to reference-count off
- /// the main thread.
- resolved_image: *mut structs::imgRequestProxy,
- /// A strong reference to the resolved URI of this image.
- resolved_uri: *mut structs::nsIURI,
- /// A few flags that are set when resolving the image or such.
- flags: LoadDataFlags,
-}
-
-impl Drop for LoadData {
- fn drop(&mut self) {
- unsafe { bindings::Gecko_LoadData_Drop(self) }
- }
-}
-
-impl Default for LoadData {
- fn default() -> Self {
- Self {
- resolved_image: std::ptr::null_mut(),
- resolved_uri: std::ptr::null_mut(),
- flags: LoadDataFlags::empty(),
- }
- }
-}
-
-/// The data for a load, or a lazy-loaded, static member that will be stored in
-/// LOAD_DATA_TABLE, keyed by the memory location of this object, which is
-/// always in the heap because it's inside the CssUrlData object.
-///
-/// This type is meant not to be used from C++ so we don't derive helper
-/// methods.
-///
-/// cbindgen:derive-helper-methods=false
-#[derive(Debug)]
-#[repr(u8, C)]
-pub enum LoadDataSource {
- /// An owned copy of the load data.
- Owned(LoadData),
- /// A lazily-resolved copy of it.
- Lazy,
-}
-
-impl LoadDataSource {
- /// Gets the load data associated with the source.
- ///
- /// This relies on the source on being in a stable location if lazy.
- #[inline]
- pub unsafe fn get(&self) -> *const LoadData {
- match *self {
- LoadDataSource::Owned(ref d) => return d,
- LoadDataSource::Lazy => {},
- }
-
- let key = LoadDataKey(self);
-
- {
- let guard = LOAD_DATA_TABLE.read().unwrap();
- if let Some(r) = guard.get(&key) {
- return &**r;
- }
- }
- let mut guard = LOAD_DATA_TABLE.write().unwrap();
- let r = guard.entry(key).or_insert_with(Default::default);
- &**r
- }
-}
-
-impl ToShmem for LoadDataSource {
- fn to_shmem(&self, _builder: &mut SharedMemoryBuilder) -> to_shmem::Result<Self> {
- Ok(ManuallyDrop::new(match self {
- LoadDataSource::Owned(..) => LoadDataSource::Lazy,
- LoadDataSource::Lazy => LoadDataSource::Lazy,
- }))
- }
-}
-
-/// A specified non-image `url()` value.
-pub type SpecifiedUrl = CssUrl;
-
-/// Clears LOAD_DATA_TABLE. Entries in this table, which are for specified URL
-/// values that come from shared memory style sheets, would otherwise persist
-/// until the end of the process and be reported as leaks.
-pub fn shutdown() {
- LOAD_DATA_TABLE.write().unwrap().clear();
-}
-
-impl ToComputedValue for SpecifiedUrl {
- type ComputedValue = ComputedUrl;
-
- #[inline]
- fn to_computed_value(&self, _: &Context) -> Self::ComputedValue {
- ComputedUrl(self.clone())
- }
-
- #[inline]
- fn from_computed_value(computed: &Self::ComputedValue) -> Self {
- computed.0.clone()
- }
-}
-
-/// A specified image `url()` value.
-#[derive(Clone, Debug, Eq, MallocSizeOf, PartialEq, SpecifiedValueInfo, ToCss, ToShmem)]
-pub struct SpecifiedImageUrl(pub SpecifiedUrl);
-
-impl SpecifiedImageUrl {
- /// Parse a URL from a string value that is a valid CSS token for a URL.
- pub fn parse_from_string(url: String, context: &ParserContext, cors_mode: CorsMode) -> Self {
- SpecifiedImageUrl(SpecifiedUrl::parse_from_string(url, context, cors_mode))
- }
-
- /// Provides an alternate method for parsing that associates the URL
- /// with anonymous CORS headers.
- pub fn parse_with_cors_mode<'i, 't>(
- context: &ParserContext,
- input: &mut Parser<'i, 't>,
- cors_mode: CorsMode,
- ) -> Result<Self, ParseError<'i>> {
- Ok(SpecifiedImageUrl(SpecifiedUrl::parse_with_cors_mode(
- context, input, cors_mode,
- )?))
- }
-}
-
-impl Parse for SpecifiedImageUrl {
- fn parse<'i, 't>(
- context: &ParserContext,
- input: &mut Parser<'i, 't>,
- ) -> Result<Self, ParseError<'i>> {
- SpecifiedUrl::parse(context, input).map(SpecifiedImageUrl)
- }
-}
-
-impl ToComputedValue for SpecifiedImageUrl {
- type ComputedValue = ComputedImageUrl;
-
- #[inline]
- fn to_computed_value(&self, context: &Context) -> Self::ComputedValue {
- ComputedImageUrl(self.0.to_computed_value(context))
- }
-
- #[inline]
- fn from_computed_value(computed: &Self::ComputedValue) -> Self {
- SpecifiedImageUrl(ToComputedValue::from_computed_value(&computed.0))
- }
-}
-
-/// The computed value of a CSS non-image `url()`.
-///
-/// The only difference between specified and computed URLs is the
-/// serialization.
-#[derive(Clone, Debug, Eq, MallocSizeOf, PartialEq)]
-#[repr(C)]
-pub struct ComputedUrl(pub SpecifiedUrl);
-
-impl ComputedUrl {
- fn serialize_with<W>(
- &self,
- function: unsafe extern "C" fn(*const Self, *mut nsCString),
- dest: &mut CssWriter<W>,
- ) -> fmt::Result
- where
- W: Write,
- {
- dest.write_str("url(")?;
- unsafe {
- let mut string = nsCString::new();
- function(self, &mut string);
- string.as_str_unchecked().to_css(dest)?;
- }
- dest.write_char(')')
- }
-}
-
-impl ToCss for ComputedUrl {
- fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
- where
- W: Write,
- {
- self.serialize_with(bindings::Gecko_GetComputedURLSpec, dest)
- }
-}
-
-/// The computed value of a CSS image `url()`.
-#[derive(Clone, Debug, Eq, MallocSizeOf, PartialEq)]
-#[repr(transparent)]
-pub struct ComputedImageUrl(pub ComputedUrl);
-
-impl ToCss for ComputedImageUrl {
- fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
- where
- W: Write,
- {
- self.0
- .serialize_with(bindings::Gecko_GetComputedImageURLSpec, dest)
- }
-}
-
-lazy_static! {
- /// A table mapping CssUrlData objects to their lazily created LoadData
- /// objects.
- static ref LOAD_DATA_TABLE: RwLock<HashMap<LoadDataKey, Box<LoadData>>> = {
- Default::default()
- };
-}
diff --git a/components/style/gecko/values.rs b/components/style/gecko/values.rs
deleted file mode 100644
index fbdb02c6bad..00000000000
--- a/components/style/gecko/values.rs
+++ /dev/null
@@ -1,72 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
-
-#![allow(unsafe_code)]
-
-//! Different kind of helpers to interact with Gecko values.
-
-use crate::color::{AbsoluteColor, ColorSpace};
-use crate::counter_style::{Symbol, Symbols};
-use crate::gecko_bindings::bindings;
-use crate::gecko_bindings::structs::CounterStylePtr;
-use crate::values::generics::CounterStyle;
-use crate::values::Either;
-use crate::Atom;
-
-/// Convert a color value to `nscolor`.
-pub fn convert_absolute_color_to_nscolor(color: &AbsoluteColor) -> u32 {
- let srgb = color.to_color_space(ColorSpace::Srgb);
- u32::from_le_bytes([
- (srgb.components.0 * 255.0).round() as u8,
- (srgb.components.1 * 255.0).round() as u8,
- (srgb.components.2 * 255.0).round() as u8,
- (srgb.alpha * 255.0).round() as u8,
- ])
-}
-
-/// Convert a given `nscolor` to a Servo AbsoluteColor value.
-pub fn convert_nscolor_to_absolute_color(color: u32) -> AbsoluteColor {
- let [r, g, b, a] = color.to_le_bytes();
- AbsoluteColor::srgb(
- r as f32 / 255.0,
- g as f32 / 255.0,
- b as f32 / 255.0,
- a as f32 / 255.0,
- )
-}
-
-impl CounterStyle {
- /// Convert this counter style to a Gecko CounterStylePtr.
- #[inline]
- pub fn to_gecko_value(&self, gecko_value: &mut CounterStylePtr) {
- unsafe { bindings::Gecko_CounterStyle_ToPtr(self, gecko_value) }
- }
-
- /// Convert Gecko CounterStylePtr to CounterStyle or String.
- pub fn from_gecko_value(gecko_value: &CounterStylePtr) -> Either<Self, String> {
- use crate::values::CustomIdent;
-
- let name = unsafe { bindings::Gecko_CounterStyle_GetName(gecko_value) };
- if !name.is_null() {
- let name = unsafe { Atom::from_raw(name) };
- debug_assert_ne!(name, atom!("none"));
- Either::First(CounterStyle::Name(CustomIdent(name)))
- } else {
- let anonymous =
- unsafe { bindings::Gecko_CounterStyle_GetAnonymous(gecko_value).as_ref() }.unwrap();
- let symbols = &anonymous.mSymbols;
- if anonymous.mSingleString {
- debug_assert_eq!(symbols.len(), 1);
- Either::Second(symbols[0].to_string())
- } else {
- let symbol_type = anonymous.mSymbolsType;
- let symbols = symbols
- .iter()
- .map(|gecko_symbol| Symbol::String(gecko_symbol.to_string().into()))
- .collect();
- Either::First(CounterStyle::Symbols(symbol_type, Symbols(symbols)))
- }
- }
- }
-}
diff --git a/components/style/gecko/wrapper.rs b/components/style/gecko/wrapper.rs
deleted file mode 100644
index 490c063fbe7..00000000000
--- a/components/style/gecko/wrapper.rs
+++ /dev/null
@@ -1,2125 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
-
-#![allow(unsafe_code)]
-
-//! Wrapper definitions on top of Gecko types in order to be used in the style
-//! system.
-//!
-//! This really follows the Servo pattern in
-//! `components/script/layout_wrapper.rs`.
-//!
-//! This theoretically should live in its own crate, but now it lives in the
-//! style system it's kind of pointless in the Stylo case, and only Servo forces
-//! the separation between the style system implementation and everything else.
-
-use crate::applicable_declarations::ApplicableDeclarationBlock;
-use crate::context::{PostAnimationTasks, QuirksMode, SharedStyleContext, UpdateAnimationsTasks};
-use crate::data::ElementData;
-use crate::dom::{LayoutIterator, NodeInfo, OpaqueNode, TDocument, TElement, TNode, TShadowRoot};
-use crate::gecko::selector_parser::{NonTSPseudoClass, PseudoElement, SelectorImpl};
-use crate::gecko::snapshot_helpers;
-use crate::gecko_bindings::bindings;
-use crate::gecko_bindings::bindings::Gecko_ElementHasAnimations;
-use crate::gecko_bindings::bindings::Gecko_ElementHasCSSAnimations;
-use crate::gecko_bindings::bindings::Gecko_ElementHasCSSTransitions;
-use crate::gecko_bindings::bindings::Gecko_ElementState;
-use crate::gecko_bindings::bindings::Gecko_GetActiveLinkAttrDeclarationBlock;
-use crate::gecko_bindings::bindings::Gecko_GetAnimationEffectCount;
-use crate::gecko_bindings::bindings::Gecko_GetAnimationRule;
-use crate::gecko_bindings::bindings::Gecko_GetExtraContentStyleDeclarations;
-use crate::gecko_bindings::bindings::Gecko_GetHTMLPresentationAttrDeclarationBlock;
-use crate::gecko_bindings::bindings::Gecko_GetStyleAttrDeclarationBlock;
-use crate::gecko_bindings::bindings::Gecko_GetUnvisitedLinkAttrDeclarationBlock;
-use crate::gecko_bindings::bindings::Gecko_GetVisitedLinkAttrDeclarationBlock;
-use crate::gecko_bindings::bindings::Gecko_IsSignificantChild;
-use crate::gecko_bindings::bindings::Gecko_MatchLang;
-use crate::gecko_bindings::bindings::Gecko_UnsetDirtyStyleAttr;
-use crate::gecko_bindings::bindings::Gecko_UpdateAnimations;
-use crate::gecko_bindings::structs;
-use crate::gecko_bindings::structs::nsChangeHint;
-use crate::gecko_bindings::structs::EffectCompositor_CascadeLevel as CascadeLevel;
-use crate::gecko_bindings::structs::ELEMENT_HANDLED_SNAPSHOT;
-use crate::gecko_bindings::structs::ELEMENT_HAS_ANIMATION_ONLY_DIRTY_DESCENDANTS_FOR_SERVO;
-use crate::gecko_bindings::structs::ELEMENT_HAS_DIRTY_DESCENDANTS_FOR_SERVO;
-use crate::gecko_bindings::structs::ELEMENT_HAS_SNAPSHOT;
-use crate::gecko_bindings::structs::NODE_DESCENDANTS_NEED_FRAMES;
-use crate::gecko_bindings::structs::NODE_NEEDS_FRAME;
-use crate::gecko_bindings::structs::{nsAtom, nsIContent, nsINode_BooleanFlag};
-use crate::gecko_bindings::structs::{nsINode as RawGeckoNode, Element as RawGeckoElement};
-use crate::global_style_data::GLOBAL_STYLE_DATA;
-use crate::invalidation::element::restyle_hints::RestyleHint;
-use crate::media_queries::Device;
-use crate::properties::animated_properties::{AnimationValue, AnimationValueMap};
-use crate::properties::{ComputedValues, LonghandId};
-use crate::properties::{Importance, PropertyDeclaration, PropertyDeclarationBlock};
-use crate::rule_tree::CascadeLevel as ServoCascadeLevel;
-use crate::selector_parser::{AttrValue, Lang};
-use crate::shared_lock::{Locked, SharedRwLock};
-use crate::string_cache::{Atom, Namespace, WeakAtom, WeakNamespace};
-use crate::stylist::CascadeData;
-use crate::values::computed::Display;
-use crate::values::{AtomIdent, AtomString};
-use crate::CaseSensitivityExt;
-use crate::LocalName;
-use app_units::Au;
-use atomic_refcell::{AtomicRef, AtomicRefCell, AtomicRefMut};
-use dom::{DocumentState, ElementState};
-use euclid::default::Size2D;
-use fxhash::FxHashMap;
-use selectors::attr::{AttrSelectorOperation, CaseSensitivity, NamespaceConstraint};
-use selectors::matching::VisitedHandlingMode;
-use selectors::matching::{ElementSelectorFlags, MatchingContext};
-use selectors::sink::Push;
-use selectors::{Element, OpaqueElement};
-use servo_arc::{Arc, ArcBorrow};
-use std::fmt;
-use std::hash::{Hash, Hasher};
-use std::mem;
-use std::ptr;
-use std::sync::atomic::{AtomicU32, Ordering};
-
-#[inline]
-fn elements_with_id<'a, 'le>(
- array: *const structs::nsTArray<*mut RawGeckoElement>,
-) -> &'a [GeckoElement<'le>] {
- unsafe {
- if array.is_null() {
- return &[];
- }
-
- let elements: &[*mut RawGeckoElement] = &**array;
-
- // NOTE(emilio): We rely on the in-memory representation of
- // GeckoElement<'ld> and *mut RawGeckoElement being the same.
- #[allow(dead_code)]
- unsafe fn static_assert() {
- mem::transmute::<*mut RawGeckoElement, GeckoElement<'static>>(0xbadc0de as *mut _);
- }
-
- mem::transmute(elements)
- }
-}
-
-/// A simple wrapper over `Document`.
-#[derive(Clone, Copy)]
-pub struct GeckoDocument<'ld>(pub &'ld structs::Document);
-
-impl<'ld> TDocument for GeckoDocument<'ld> {
- type ConcreteNode = GeckoNode<'ld>;
-
- #[inline]
- fn as_node(&self) -> Self::ConcreteNode {
- GeckoNode(&self.0._base)
- }
-
- #[inline]
- fn is_html_document(&self) -> bool {
- self.0.mType == structs::Document_Type::eHTML
- }
-
- #[inline]
- fn quirks_mode(&self) -> QuirksMode {
- self.0.mCompatMode.into()
- }
-
- #[inline]
- fn elements_with_id<'a>(&self, id: &AtomIdent) -> Result<&'a [GeckoElement<'ld>], ()>
- where
- Self: 'a,
- {
- Ok(elements_with_id(unsafe {
- bindings::Gecko_Document_GetElementsWithId(self.0, id.as_ptr())
- }))
- }
-
- fn shared_lock(&self) -> &SharedRwLock {
- &GLOBAL_STYLE_DATA.shared_lock
- }
-}
-
-/// A simple wrapper over `ShadowRoot`.
-#[derive(Clone, Copy)]
-pub struct GeckoShadowRoot<'lr>(pub &'lr structs::ShadowRoot);
-
-impl<'ln> fmt::Debug for GeckoShadowRoot<'ln> {
- fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
- // TODO(emilio): Maybe print the host or something?
- write!(f, "<shadow-root> ({:#x})", self.as_node().opaque().0)
- }
-}
-
-impl<'lr> PartialEq for GeckoShadowRoot<'lr> {
- #[inline]
- fn eq(&self, other: &Self) -> bool {
- self.0 as *const _ == other.0 as *const _
- }
-}
-
-impl<'lr> TShadowRoot for GeckoShadowRoot<'lr> {
- type ConcreteNode = GeckoNode<'lr>;
-
- #[inline]
- fn as_node(&self) -> Self::ConcreteNode {
- GeckoNode(&self.0._base._base._base._base)
- }
-
- #[inline]
- fn host(&self) -> GeckoElement<'lr> {
- GeckoElement(unsafe { &*self.0._base.mHost.mRawPtr })
- }
-
- #[inline]
- fn style_data<'a>(&self) -> Option<&'a CascadeData>
- where
- Self: 'a,
- {
- let author_styles = unsafe { self.0.mServoStyles.mPtr.as_ref()? };
- Some(&author_styles.data)
- }
-
- #[inline]
- fn elements_with_id<'a>(&self, id: &AtomIdent) -> Result<&'a [GeckoElement<'lr>], ()>
- where
- Self: 'a,
- {
- Ok(elements_with_id(unsafe {
- bindings::Gecko_ShadowRoot_GetElementsWithId(self.0, id.as_ptr())
- }))
- }
-
- #[inline]
- fn parts<'a>(&self) -> &[<Self::ConcreteNode as TNode>::ConcreteElement]
- where
- Self: 'a,
- {
- let slice: &[*const RawGeckoElement] = &*self.0.mParts;
-
- #[allow(dead_code)]
- unsafe fn static_assert() {
- mem::transmute::<*const RawGeckoElement, GeckoElement<'static>>(0xbadc0de as *const _);
- }
-
- unsafe { mem::transmute(slice) }
- }
-}
-
-/// A simple wrapper over a non-null Gecko node (`nsINode`) pointer.
-///
-/// Important: We don't currently refcount the DOM, because the wrapper lifetime
-/// magic guarantees that our LayoutFoo references won't outlive the root, and
-/// we don't mutate any of the references on the Gecko side during restyle.
-///
-/// We could implement refcounting if need be (at a potentially non-trivial
-/// performance cost) by implementing Drop and making LayoutFoo non-Copy.
-#[derive(Clone, Copy)]
-pub struct GeckoNode<'ln>(pub &'ln RawGeckoNode);
-
-impl<'ln> PartialEq for GeckoNode<'ln> {
- #[inline]
- fn eq(&self, other: &Self) -> bool {
- self.0 as *const _ == other.0 as *const _
- }
-}
-
-impl<'ln> fmt::Debug for GeckoNode<'ln> {
- fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
- if let Some(el) = self.as_element() {
- return el.fmt(f);
- }
-
- if self.is_text_node() {
- return write!(f, "<text node> ({:#x})", self.opaque().0);
- }
-
- if self.is_document() {
- return write!(f, "<document> ({:#x})", self.opaque().0);
- }
-
- if let Some(sr) = self.as_shadow_root() {
- return sr.fmt(f);
- }
-
- write!(f, "<non-text node> ({:#x})", self.opaque().0)
- }
-}
-
-impl<'ln> GeckoNode<'ln> {
- #[inline]
- fn is_document(&self) -> bool {
- // This is a DOM constant that isn't going to change.
- const DOCUMENT_NODE: u16 = 9;
- self.node_info().mInner.mNodeType == DOCUMENT_NODE
- }
-
- #[inline]
- fn is_shadow_root(&self) -> bool {
- self.is_in_shadow_tree() && self.parent_node().is_none()
- }
-
- #[inline]
- fn from_content(content: &'ln nsIContent) -> Self {
- GeckoNode(&content._base)
- }
-
- #[inline]
- fn set_flags(&self, flags: u32) {
- self.flags_atomic().fetch_or(flags, Ordering::Relaxed);
- }
-
- #[inline]
- fn flags_atomic(&self) -> &AtomicU32 {
- use std::cell::Cell;
- let flags: &Cell<u32> = &(self.0)._base._base_1.mFlags;
-
- #[allow(dead_code)]
- fn static_assert() {
- let _: [u8; std::mem::size_of::<Cell<u32>>()] = [0u8; std::mem::size_of::<AtomicU32>()];
- let _: [u8; std::mem::align_of::<Cell<u32>>()] =
- [0u8; std::mem::align_of::<AtomicU32>()];
- }
-
- // Rust doesn't provide standalone atomic functions like GCC/clang do
- // (via the atomic intrinsics) or via std::atomic_ref, but it guarantees
- // that the memory representation of u32 and AtomicU32 matches:
- // https://doc.rust-lang.org/std/sync/atomic/struct.AtomicU32.html
- unsafe { std::mem::transmute::<&Cell<u32>, &AtomicU32>(flags) }
- }
-
- #[inline]
- fn flags(&self) -> u32 {
- self.flags_atomic().load(Ordering::Relaxed)
- }
-
- #[inline]
- fn node_info(&self) -> &structs::NodeInfo {
- debug_assert!(!self.0.mNodeInfo.mRawPtr.is_null());
- unsafe { &*self.0.mNodeInfo.mRawPtr }
- }
-
- // These live in different locations depending on processor architecture.
- #[cfg(target_pointer_width = "64")]
- #[inline]
- fn bool_flags(&self) -> u32 {
- (self.0)._base._base_1.mBoolFlags
- }
-
- #[cfg(target_pointer_width = "32")]
- #[inline]
- fn bool_flags(&self) -> u32 {
- (self.0).mBoolFlags
- }
-
- #[inline]
- fn get_bool_flag(&self, flag: nsINode_BooleanFlag) -> bool {
- self.bool_flags() & (1u32 << flag as u32) != 0
- }
-
- /// This logic is duplicate in Gecko's nsINode::IsInShadowTree().
- #[inline]
- fn is_in_shadow_tree(&self) -> bool {
- use crate::gecko_bindings::structs::NODE_IS_IN_SHADOW_TREE;
- self.flags() & NODE_IS_IN_SHADOW_TREE != 0
- }
-
- /// Returns true if we know for sure that `flattened_tree_parent` and `parent_node` return the
- /// same thing.
- ///
- /// TODO(emilio): Measure and consider not doing this fast-path, it's only a function call and
- /// from profiles it seems that keeping this fast path makes the compiler not inline
- /// `flattened_tree_parent` as a whole, so we're not gaining much either.
- #[inline]
- fn flattened_tree_parent_is_parent(&self) -> bool {
- use crate::gecko_bindings::structs::*;
- let flags = self.flags();
-
- let parent = match self.parent_node() {
- Some(p) => p,
- None => return true,
- };
-
- if parent.is_shadow_root() {
- return false;
- }
-
- if let Some(parent) = parent.as_element() {
- if flags & NODE_IS_NATIVE_ANONYMOUS_ROOT != 0 && parent.is_root() {
- return false;
- }
- if parent.shadow_root().is_some() || parent.is_html_slot_element() {
- return false;
- }
- }
-
- true
- }
-
- #[inline]
- fn flattened_tree_parent(&self) -> Option<Self> {
- if self.flattened_tree_parent_is_parent() {
- debug_assert_eq!(
- unsafe {
- bindings::Gecko_GetFlattenedTreeParentNode(self.0)
- .as_ref()
- .map(GeckoNode)
- },
- self.parent_node(),
- "Fast path stopped holding!"
- );
- return self.parent_node();
- }
-
- // NOTE(emilio): If this call is too expensive, we could manually inline more aggressively.
- unsafe {
- bindings::Gecko_GetFlattenedTreeParentNode(self.0)
- .as_ref()
- .map(GeckoNode)
- }
- }
-
- #[inline]
- fn contains_non_whitespace_content(&self) -> bool {
- unsafe { Gecko_IsSignificantChild(self.0, false) }
- }
-}
-
-impl<'ln> NodeInfo for GeckoNode<'ln> {
- #[inline]
- fn is_element(&self) -> bool {
- self.get_bool_flag(nsINode_BooleanFlag::NodeIsElement)
- }
-
- fn is_text_node(&self) -> bool {
- // This is a DOM constant that isn't going to change.
- const TEXT_NODE: u16 = 3;
- self.node_info().mInner.mNodeType == TEXT_NODE
- }
-}
-
-impl<'ln> TNode for GeckoNode<'ln> {
- type ConcreteDocument = GeckoDocument<'ln>;
- type ConcreteShadowRoot = GeckoShadowRoot<'ln>;
- type ConcreteElement = GeckoElement<'ln>;
-
- #[inline]
- fn parent_node(&self) -> Option<Self> {
- unsafe { self.0.mParent.as_ref().map(GeckoNode) }
- }
-
- #[inline]
- fn first_child(&self) -> Option<Self> {
- unsafe {
- self.0
- .mFirstChild
- .raw()
- .as_ref()
- .map(GeckoNode::from_content)
- }
- }
-
- #[inline]
- fn last_child(&self) -> Option<Self> {
- unsafe { bindings::Gecko_GetLastChild(self.0).as_ref().map(GeckoNode) }
- }
-
- #[inline]
- fn prev_sibling(&self) -> Option<Self> {
- unsafe {
- let prev_or_last = GeckoNode::from_content(self.0.mPreviousOrLastSibling.as_ref()?);
- if prev_or_last.0.mNextSibling.raw().is_null() {
- return None;
- }
- Some(prev_or_last)
- }
- }
-
- #[inline]
- fn next_sibling(&self) -> Option<Self> {
- unsafe {
- self.0
- .mNextSibling
- .raw()
- .as_ref()
- .map(GeckoNode::from_content)
- }
- }
-
- #[inline]
- fn owner_doc(&self) -> Self::ConcreteDocument {
- debug_assert!(!self.node_info().mDocument.is_null());
- GeckoDocument(unsafe { &*self.node_info().mDocument })
- }
-
- #[inline]
- fn is_in_document(&self) -> bool {
- self.get_bool_flag(nsINode_BooleanFlag::IsInDocument)
- }
-
- fn traversal_parent(&self) -> Option<GeckoElement<'ln>> {
- self.flattened_tree_parent().and_then(|n| n.as_element())
- }
-
- #[inline]
- fn opaque(&self) -> OpaqueNode {
- let ptr: usize = self.0 as *const _ as usize;
- OpaqueNode(ptr)
- }
-
- fn debug_id(self) -> usize {
- unimplemented!()
- }
-
- #[inline]
- fn as_element(&self) -> Option<GeckoElement<'ln>> {
- if !self.is_element() {
- return None;
- }
-
- Some(GeckoElement(unsafe {
- &*(self.0 as *const _ as *const RawGeckoElement)
- }))
- }
-
- #[inline]
- fn as_document(&self) -> Option<Self::ConcreteDocument> {
- if !self.is_document() {
- return None;
- }
-
- debug_assert_eq!(self.owner_doc().as_node(), *self, "How?");
- Some(self.owner_doc())
- }
-
- #[inline]
- fn as_shadow_root(&self) -> Option<Self::ConcreteShadowRoot> {
- if !self.is_shadow_root() {
- return None;
- }
-
- Some(GeckoShadowRoot(unsafe {
- &*(self.0 as *const _ as *const structs::ShadowRoot)
- }))
- }
-}
-
-/// A wrapper on top of two kind of iterators, depending on the parent being
-/// iterated.
-///
-/// We generally iterate children by traversing the light-tree siblings of the
-/// first child like Servo does.
-///
-/// However, for nodes with anonymous children, we use a custom (heavier-weight)
-/// Gecko-implemented iterator.
-///
-/// FIXME(emilio): If we take into account shadow DOM, we're going to need the
-/// flat tree pretty much always. We can try to optimize the case where there's
-/// no shadow root sibling, probably.
-pub enum GeckoChildrenIterator<'a> {
- /// A simple iterator that tracks the current node being iterated and
- /// replaces it with the next sibling when requested.
- Current(Option<GeckoNode<'a>>),
- /// A Gecko-implemented iterator we need to drop appropriately.
- GeckoIterator(structs::StyleChildrenIterator),
-}
-
-impl<'a> Drop for GeckoChildrenIterator<'a> {
- fn drop(&mut self) {
- if let GeckoChildrenIterator::GeckoIterator(ref mut it) = *self {
- unsafe {
- bindings::Gecko_DestroyStyleChildrenIterator(it);
- }
- }
- }
-}
-
-impl<'a> Iterator for GeckoChildrenIterator<'a> {
- type Item = GeckoNode<'a>;
- fn next(&mut self) -> Option<GeckoNode<'a>> {
- match *self {
- GeckoChildrenIterator::Current(curr) => {
- let next = curr.and_then(|node| node.next_sibling());
- *self = GeckoChildrenIterator::Current(next);
- curr
- },
- GeckoChildrenIterator::GeckoIterator(ref mut it) => unsafe {
- // We do this unsafe lengthening of the lifetime here because
- // structs::StyleChildrenIterator is actually StyleChildrenIterator<'a>,
- // however we can't express this easily with bindgen, and it would
- // introduce functions with two input lifetimes into bindgen,
- // which would be out of scope for elision.
- bindings::Gecko_GetNextStyleChild(&mut *(it as *mut _))
- .as_ref()
- .map(GeckoNode)
- },
- }
- }
-}
-
-/// A simple wrapper over a non-null Gecko `Element` pointer.
-#[derive(Clone, Copy)]
-pub struct GeckoElement<'le>(pub &'le RawGeckoElement);
-
-impl<'le> fmt::Debug for GeckoElement<'le> {
- fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
- use nsstring::nsCString;
-
- write!(f, "<{}", self.local_name())?;
-
- let mut attrs = nsCString::new();
- unsafe {
- bindings::Gecko_Element_DebugListAttributes(self.0, &mut attrs);
- }
- write!(f, "{}", attrs)?;
- write!(f, "> ({:#x})", self.as_node().opaque().0)
- }
-}
-
-impl<'le> GeckoElement<'le> {
- /// Gets the raw `ElementData` refcell for the element.
- #[inline(always)]
- pub fn get_data(&self) -> Option<&AtomicRefCell<ElementData>> {
- unsafe { self.0.mServoData.get().as_ref() }
- }
-
- /// Returns whether any animation applies to this element.
- #[inline]
- pub fn has_any_animation(&self) -> bool {
- self.may_have_animations() && unsafe { Gecko_ElementHasAnimations(self.0) }
- }
-
- #[inline(always)]
- fn attrs(&self) -> Option<&structs::AttrArray_Impl> {
- unsafe { self.0.mAttrs.mImpl.mPtr.as_ref() }
- }
-
- #[inline(always)]
- fn non_mapped_attrs(&self) -> &[structs::AttrArray_InternalAttr] {
- let attrs = match self.attrs() {
- Some(attrs) => attrs,
- None => return &[],
- };
- unsafe {
- attrs.mBuffer.as_slice(attrs.mAttrCount as usize)
- }
- }
-
- #[inline(always)]
- fn mapped_attrs(&self) -> &[structs::AttrArray_InternalAttr] {
- let attrs = match self.attrs() {
- Some(attrs) => attrs,
- None => return &[],
- };
- unsafe {
- let attrs = match attrs.mMappedAttrs.as_ref() {
- Some(attrs) => attrs,
- None => return &[],
- };
-
- attrs.mBuffer.as_slice(attrs.mAttrCount as usize)
- }
- }
-
- #[inline]
- fn iter_attrs(&self) -> impl Iterator<Item = &structs::AttrArray_InternalAttr> {
- self.non_mapped_attrs().iter().chain(self.mapped_attrs().iter())
- }
-
- #[inline(always)]
- fn get_part_attr(&self) -> Option<&structs::nsAttrValue> {
- if !self.has_part_attr() {
- return None;
- }
- snapshot_helpers::find_attr(self.non_mapped_attrs(), &atom!("part"))
- }
-
- #[inline(always)]
- fn get_class_attr(&self) -> Option<&structs::nsAttrValue> {
- if !self.may_have_class() {
- return None;
- }
-
- if self.is_svg_element() {
- let svg_class = unsafe { bindings::Gecko_GetSVGAnimatedClass(self.0).as_ref() };
- if let Some(c) = svg_class {
- return Some(c);
- }
- }
-
- snapshot_helpers::find_attr(self.non_mapped_attrs(), &atom!("class"))
- }
-
- #[inline]
- fn may_have_anonymous_children(&self) -> bool {
- self.as_node()
- .get_bool_flag(nsINode_BooleanFlag::ElementMayHaveAnonymousChildren)
- }
-
- #[inline]
- fn flags(&self) -> u32 {
- self.as_node().flags()
- }
-
- #[inline]
- fn set_flags(&self, flags: u32) {
- self.as_node().set_flags(flags);
- }
-
- #[inline]
- unsafe fn unset_flags(&self, flags: u32) {
- self.as_node()
- .flags_atomic()
- .fetch_and(!flags, Ordering::Relaxed);
- }
-
- /// Returns true if this element has descendants for lazy frame construction.
- #[inline]
- pub fn descendants_need_frames(&self) -> bool {
- self.flags() & NODE_DESCENDANTS_NEED_FRAMES != 0
- }
-
- /// Returns true if this element needs lazy frame construction.
- #[inline]
- pub fn needs_frame(&self) -> bool {
- self.flags() & NODE_NEEDS_FRAME != 0
- }
-
- /// Returns a reference to the DOM slots for this Element, if they exist.
- #[inline]
- fn dom_slots(&self) -> Option<&structs::FragmentOrElement_nsDOMSlots> {
- let slots = self.as_node().0.mSlots as *const structs::FragmentOrElement_nsDOMSlots;
- unsafe { slots.as_ref() }
- }
-
- /// Returns a reference to the extended DOM slots for this Element.
- #[inline]
- fn extended_slots(&self) -> Option<&structs::FragmentOrElement_nsExtendedDOMSlots> {
- self.dom_slots().and_then(|s| unsafe {
- // For the bit usage, see nsContentSlots::GetExtendedSlots.
- let e_slots = s._base.mExtendedSlots &
- !structs::nsIContent_nsContentSlots_sNonOwningExtendedSlotsFlag;
- (e_slots as *const structs::FragmentOrElement_nsExtendedDOMSlots).as_ref()
- })
- }
-
- #[inline]
- fn namespace_id(&self) -> i32 {
- self.as_node().node_info().mInner.mNamespaceID
- }
-
- #[inline]
- fn has_id(&self) -> bool {
- self.as_node()
- .get_bool_flag(nsINode_BooleanFlag::ElementHasID)
- }
-
- #[inline]
- fn state_internal(&self) -> u64 {
- if !self
- .as_node()
- .get_bool_flag(nsINode_BooleanFlag::ElementHasLockedStyleStates)
- {
- return self.0.mState.bits;
- }
- unsafe { Gecko_ElementState(self.0) }
- }
-
- #[inline]
- fn document_state(&self) -> DocumentState {
- DocumentState::from_bits_truncate(self.as_node().owner_doc().0.mDocumentState.bits)
- }
-
- #[inline]
- fn may_have_class(&self) -> bool {
- self.as_node()
- .get_bool_flag(nsINode_BooleanFlag::ElementMayHaveClass)
- }
-
- #[inline]
- fn has_properties(&self) -> bool {
- use crate::gecko_bindings::structs::NODE_HAS_PROPERTIES;
-
- self.flags() & NODE_HAS_PROPERTIES != 0
- }
-
- #[inline]
- fn before_or_after_pseudo(&self, is_before: bool) -> Option<Self> {
- if !self.has_properties() {
- return None;
- }
-
- unsafe {
- bindings::Gecko_GetBeforeOrAfterPseudo(self.0, is_before)
- .as_ref()
- .map(GeckoElement)
- }
- }
-
- #[inline]
- fn may_have_style_attribute(&self) -> bool {
- self.as_node()
- .get_bool_flag(nsINode_BooleanFlag::ElementMayHaveStyle)
- }
-
- /// Only safe to call on the main thread, with exclusive access to the
- /// element and its ancestors.
- ///
- /// This function is also called after display property changed for SMIL
- /// animation.
- ///
- /// Also this function schedules style flush.
- pub unsafe fn note_explicit_hints(&self, restyle_hint: RestyleHint, change_hint: nsChangeHint) {
- use crate::gecko::restyle_damage::GeckoRestyleDamage;
-
- let damage = GeckoRestyleDamage::new(change_hint);
- debug!(
- "note_explicit_hints: {:?}, restyle_hint={:?}, change_hint={:?}",
- self, restyle_hint, change_hint
- );
-
- debug_assert!(
- !(restyle_hint.has_animation_hint() && restyle_hint.has_non_animation_hint()),
- "Animation restyle hints should not appear with non-animation restyle hints"
- );
-
- let mut data = match self.mutate_data() {
- Some(d) => d,
- None => {
- debug!("(Element not styled, discarding hints)");
- return;
- },
- };
-
- debug_assert!(data.has_styles(), "how?");
-
- // Propagate the bit up the chain.
- if restyle_hint.has_animation_hint() {
- bindings::Gecko_NoteAnimationOnlyDirtyElement(self.0);
- } else {
- bindings::Gecko_NoteDirtyElement(self.0);
- }
-
- data.hint.insert(restyle_hint);
- data.damage |= damage;
- }
-
- /// This logic is duplicated in Gecko's nsIContent::IsRootOfNativeAnonymousSubtree.
- #[inline]
- fn is_root_of_native_anonymous_subtree(&self) -> bool {
- use crate::gecko_bindings::structs::NODE_IS_NATIVE_ANONYMOUS_ROOT;
- return self.flags() & NODE_IS_NATIVE_ANONYMOUS_ROOT != 0;
- }
-
- /// Returns true if this node is the shadow root of an use-element shadow tree.
- #[inline]
- fn is_root_of_use_element_shadow_tree(&self) -> bool {
- if !self.as_node().is_in_shadow_tree() {
- return false;
- }
- if !self.parent_node_is_shadow_root() {
- return false;
- }
- let host = self.containing_shadow_host().unwrap();
- host.is_svg_element() && host.local_name() == &**local_name!("use")
- }
-
- fn css_transitions_info(&self) -> FxHashMap<LonghandId, Arc<AnimationValue>> {
- use crate::gecko_bindings::bindings::Gecko_ElementTransitions_EndValueAt;
- use crate::gecko_bindings::bindings::Gecko_ElementTransitions_Length;
-
- let collection_length = unsafe { Gecko_ElementTransitions_Length(self.0) } as usize;
- let mut map = FxHashMap::with_capacity_and_hasher(collection_length, Default::default());
-
- for i in 0..collection_length {
- let end_value =
- unsafe { Arc::from_raw_addrefed(Gecko_ElementTransitions_EndValueAt(self.0, i)) };
- let property = end_value.id();
- debug_assert!(!property.is_logical());
- map.insert(property, end_value);
- }
- map
- }
-
- fn needs_transitions_update_per_property(
- &self,
- longhand_id: LonghandId,
- combined_duration_seconds: f32,
- before_change_style: &ComputedValues,
- after_change_style: &ComputedValues,
- existing_transitions: &FxHashMap<LonghandId, Arc<AnimationValue>>,
- ) -> bool {
- use crate::values::animated::{Animate, Procedure};
- debug_assert!(!longhand_id.is_logical());
-
- // If there is an existing transition, update only if the end value
- // differs.
- //
- // If the end value has not changed, we should leave the currently
- // running transition as-is since we don't want to interrupt its timing
- // function.
- if let Some(ref existing) = existing_transitions.get(&longhand_id) {
- let after_value =
- AnimationValue::from_computed_values(longhand_id, after_change_style).unwrap();
-
- return ***existing != after_value;
- }
-
- let from = AnimationValue::from_computed_values(longhand_id, before_change_style);
- let to = AnimationValue::from_computed_values(longhand_id, after_change_style);
-
- debug_assert_eq!(to.is_some(), from.is_some());
-
- combined_duration_seconds > 0.0f32 &&
- from != to &&
- from.unwrap()
- .animate(
- to.as_ref().unwrap(),
- Procedure::Interpolate { progress: 0.5 },
- )
- .is_ok()
- }
-}
-
-/// Converts flags from the layout used by rust-selectors to the layout used
-/// by Gecko. We could align these and then do this without conditionals, but
-/// it's probably not worth the trouble.
-fn selector_flags_to_node_flags(flags: ElementSelectorFlags) -> u32 {
- use crate::gecko_bindings::structs::*;
- let mut gecko_flags = 0u32;
- if flags.contains(ElementSelectorFlags::HAS_SLOW_SELECTOR) {
- gecko_flags |= NODE_HAS_SLOW_SELECTOR;
- }
- if flags.contains(ElementSelectorFlags::HAS_SLOW_SELECTOR_LATER_SIBLINGS) {
- gecko_flags |= NODE_HAS_SLOW_SELECTOR_LATER_SIBLINGS;
- }
- if flags.contains(ElementSelectorFlags::HAS_SLOW_SELECTOR_NTH_OF) {
- gecko_flags |= NODE_HAS_SLOW_SELECTOR_NTH_OF;
- }
- if flags.contains(ElementSelectorFlags::HAS_EDGE_CHILD_SELECTOR) {
- gecko_flags |= NODE_HAS_EDGE_CHILD_SELECTOR;
- }
- if flags.contains(ElementSelectorFlags::HAS_EMPTY_SELECTOR) {
- gecko_flags |= NODE_HAS_EMPTY_SELECTOR;
- }
-
- gecko_flags
-}
-
-fn get_animation_rule(
- element: &GeckoElement,
- cascade_level: CascadeLevel,
-) -> Option<Arc<Locked<PropertyDeclarationBlock>>> {
- use crate::properties::longhands::ANIMATABLE_PROPERTY_COUNT;
-
- // There's a very rough correlation between the number of effects
- // (animations) on an element and the number of properties it is likely to
- // animate, so we use that as an initial guess for the size of the
- // AnimationValueMap in order to reduce the number of re-allocations needed.
- let effect_count = unsafe { Gecko_GetAnimationEffectCount(element.0) };
- // Also, we should try to reuse the PDB, to avoid creating extra rule nodes.
- let mut animation_values = AnimationValueMap::with_capacity_and_hasher(
- effect_count.min(ANIMATABLE_PROPERTY_COUNT),
- Default::default(),
- );
- if unsafe { Gecko_GetAnimationRule(element.0, cascade_level, &mut animation_values) } {
- let shared_lock = &GLOBAL_STYLE_DATA.shared_lock;
- Some(Arc::new(shared_lock.wrap(
- PropertyDeclarationBlock::from_animation_value_map(&animation_values),
- )))
- } else {
- None
- }
-}
-
-/// Turns a gecko namespace id into an atom. Might panic if you pass any random thing that isn't a
-/// namespace id.
-#[inline(always)]
-pub unsafe fn namespace_id_to_atom(id: i32) -> *mut nsAtom {
- unsafe {
- let namespace_manager = structs::nsNameSpaceManager_sInstance.mRawPtr;
- (*namespace_manager).mURIArray[id as usize].mRawPtr
- }
-}
-
-impl<'le> TElement for GeckoElement<'le> {
- type ConcreteNode = GeckoNode<'le>;
- type TraversalChildrenIterator = GeckoChildrenIterator<'le>;
-
- fn inheritance_parent(&self) -> Option<Self> {
- if self.is_pseudo_element() {
- return self.pseudo_element_originating_element();
- }
-
- self.as_node()
- .flattened_tree_parent()
- .and_then(|n| n.as_element())
- }
-
- fn traversal_children(&self) -> LayoutIterator<GeckoChildrenIterator<'le>> {
- // This condition is similar to the check that
- // StyleChildrenIterator::IsNeeded does, except that it might return
- // true if we used to (but no longer) have anonymous content from
- // ::before/::after, or nsIAnonymousContentCreators.
- if self.is_html_slot_element() ||
- self.shadow_root().is_some() ||
- self.may_have_anonymous_children()
- {
- unsafe {
- let mut iter: structs::StyleChildrenIterator = ::std::mem::zeroed();
- bindings::Gecko_ConstructStyleChildrenIterator(self.0, &mut iter);
- return LayoutIterator(GeckoChildrenIterator::GeckoIterator(iter));
- }
- }
-
- LayoutIterator(GeckoChildrenIterator::Current(self.as_node().first_child()))
- }
-
- fn before_pseudo_element(&self) -> Option<Self> {
- self.before_or_after_pseudo(/* is_before = */ true)
- }
-
- fn after_pseudo_element(&self) -> Option<Self> {
- self.before_or_after_pseudo(/* is_before = */ false)
- }
-
- fn marker_pseudo_element(&self) -> Option<Self> {
- if !self.has_properties() {
- return None;
- }
-
- unsafe {
- bindings::Gecko_GetMarkerPseudo(self.0)
- .as_ref()
- .map(GeckoElement)
- }
- }
-
- #[inline]
- fn is_html_element(&self) -> bool {
- self.namespace_id() == structs::kNameSpaceID_XHTML as i32
- }
-
- #[inline]
- fn is_mathml_element(&self) -> bool {
- self.namespace_id() == structs::kNameSpaceID_MathML as i32
- }
-
- #[inline]
- fn is_svg_element(&self) -> bool {
- self.namespace_id() == structs::kNameSpaceID_SVG as i32
- }
-
- #[inline]
- fn is_xul_element(&self) -> bool {
- self.namespace_id() == structs::root::kNameSpaceID_XUL as i32
- }
-
- #[inline]
- fn local_name(&self) -> &WeakAtom {
- unsafe { WeakAtom::new(self.as_node().node_info().mInner.mName) }
- }
-
- #[inline]
- fn namespace(&self) -> &WeakNamespace {
- unsafe { WeakNamespace::new(namespace_id_to_atom(self.namespace_id())) }
- }
-
- #[inline]
- fn query_container_size(&self, display: &Display) -> Size2D<Option<Au>> {
- // If an element gets 'display: contents' and its nsIFrame has not been removed yet,
- // Gecko_GetQueryContainerSize will not notice that it can't have size containment.
- // Other cases like 'display: inline' will be handled once the new nsIFrame is created.
- if display.is_contents() {
- return Size2D::new(None, None);
- }
-
- let mut width = -1;
- let mut height = -1;
- unsafe {
- bindings::Gecko_GetQueryContainerSize(self.0, &mut width, &mut height);
- }
- Size2D::new(
- if width >= 0 { Some(Au(width)) } else { None },
- if height >= 0 { Some(Au(height)) } else { None },
- )
- }
-
- /// Return the list of slotted nodes of this node.
- #[inline]
- fn slotted_nodes(&self) -> &[Self::ConcreteNode] {
- if !self.is_html_slot_element() || !self.as_node().is_in_shadow_tree() {
- return &[];
- }
-
- let slot: &structs::HTMLSlotElement = unsafe { mem::transmute(self.0) };
-
- if cfg!(debug_assertions) {
- let base: &RawGeckoElement = &slot._base._base._base._base;
- assert_eq!(base as *const _, self.0 as *const _, "Bad cast");
- }
-
- // FIXME(emilio): Workaround a bindgen bug on Android that causes
- // mAssignedNodes to be at the wrong offset. See bug 1466406.
- //
- // Bug 1466580 tracks running the Android layout tests on automation.
- //
- // The actual bindgen bug still needs reduction.
- let assigned_nodes: &[structs::RefPtr<structs::nsINode>] = if !cfg!(target_os = "android") {
- debug_assert_eq!(
- unsafe { bindings::Gecko_GetAssignedNodes(self.0) },
- &slot.mAssignedNodes as *const _,
- );
-
- &*slot.mAssignedNodes
- } else {
- unsafe { &**bindings::Gecko_GetAssignedNodes(self.0) }
- };
-
- debug_assert_eq!(
- mem::size_of::<structs::RefPtr<structs::nsINode>>(),
- mem::size_of::<Self::ConcreteNode>(),
- "Bad cast!"
- );
-
- unsafe { mem::transmute(assigned_nodes) }
- }
-
- #[inline]
- fn shadow_root(&self) -> Option<GeckoShadowRoot<'le>> {
- let slots = self.extended_slots()?;
- unsafe { slots.mShadowRoot.mRawPtr.as_ref().map(GeckoShadowRoot) }
- }
-
- #[inline]
- fn containing_shadow(&self) -> Option<GeckoShadowRoot<'le>> {
- let slots = self.extended_slots()?;
- unsafe {
- slots
- ._base
- .mContainingShadow
- .mRawPtr
- .as_ref()
- .map(GeckoShadowRoot)
- }
- }
-
- fn each_anonymous_content_child<F>(&self, mut f: F)
- where
- F: FnMut(Self),
- {
- if !self.may_have_anonymous_children() {
- return;
- }
-
- let array: *mut structs::nsTArray<*mut nsIContent> =
- unsafe { bindings::Gecko_GetAnonymousContentForElement(self.0) };
-
- if array.is_null() {
- return;
- }
-
- for content in unsafe { &**array } {
- let node = GeckoNode::from_content(unsafe { &**content });
- let element = match node.as_element() {
- Some(e) => e,
- None => continue,
- };
-
- f(element);
- }
-
- unsafe { bindings::Gecko_DestroyAnonymousContentList(array) };
- }
-
- #[inline]
- fn as_node(&self) -> Self::ConcreteNode {
- unsafe { GeckoNode(&*(self.0 as *const _ as *const RawGeckoNode)) }
- }
-
- fn owner_doc_matches_for_testing(&self, device: &Device) -> bool {
- self.as_node().owner_doc().0 as *const structs::Document == device.document() as *const _
- }
-
- fn style_attribute(&self) -> Option<ArcBorrow<Locked<PropertyDeclarationBlock>>> {
- if !self.may_have_style_attribute() {
- return None;
- }
-
- unsafe {
- let declarations = Gecko_GetStyleAttrDeclarationBlock(self.0).as_ref()?;
- Some(ArcBorrow::from_ref(declarations))
- }
- }
-
- fn unset_dirty_style_attribute(&self) {
- if !self.may_have_style_attribute() {
- return;
- }
-
- unsafe { Gecko_UnsetDirtyStyleAttr(self.0) };
- }
-
- fn smil_override(&self) -> Option<ArcBorrow<Locked<PropertyDeclarationBlock>>> {
- unsafe {
- let slots = self.extended_slots()?;
-
- let declaration: &structs::DeclarationBlock =
- slots.mSMILOverrideStyleDeclaration.mRawPtr.as_ref()?;
-
- let raw: &structs::StyleLockedDeclarationBlock = declaration.mRaw.mRawPtr.as_ref()?;
- Some(ArcBorrow::from_ref(raw))
- }
- }
-
- fn animation_rule(
- &self,
- _: &SharedStyleContext,
- ) -> Option<Arc<Locked<PropertyDeclarationBlock>>> {
- get_animation_rule(self, CascadeLevel::Animations)
- }
-
- fn transition_rule(
- &self,
- _: &SharedStyleContext,
- ) -> Option<Arc<Locked<PropertyDeclarationBlock>>> {
- get_animation_rule(self, CascadeLevel::Transitions)
- }
-
- #[inline]
- fn state(&self) -> ElementState {
- ElementState::from_bits_truncate(self.state_internal())
- }
-
- #[inline]
- fn has_part_attr(&self) -> bool {
- self.as_node()
- .get_bool_flag(nsINode_BooleanFlag::ElementHasPart)
- }
-
- #[inline]
- fn exports_any_part(&self) -> bool {
- snapshot_helpers::find_attr(self.non_mapped_attrs(), &atom!("exportparts")).is_some()
- }
-
- // FIXME(emilio): we should probably just return a reference to the Atom.
- #[inline]
- fn id(&self) -> Option<&WeakAtom> {
- if !self.has_id() {
- return None;
- }
-
- snapshot_helpers::get_id(self.non_mapped_attrs())
- }
-
- fn each_attr_name<F>(&self, mut callback: F)
- where
- F: FnMut(&AtomIdent),
- {
- for attr in self.iter_attrs() {
- unsafe {
- AtomIdent::with(attr.mName.name(), |a| callback(a))
- }
- }
- }
-
- fn each_class<F>(&self, callback: F)
- where
- F: FnMut(&AtomIdent),
- {
- let attr = match self.get_class_attr() {
- Some(c) => c,
- None => return,
- };
-
- snapshot_helpers::each_class_or_part(attr, callback)
- }
-
- #[inline]
- fn each_exported_part<F>(&self, name: &AtomIdent, callback: F)
- where
- F: FnMut(&AtomIdent),
- {
- snapshot_helpers::each_exported_part(self.non_mapped_attrs(), name, callback)
- }
-
- fn each_part<F>(&self, callback: F)
- where
- F: FnMut(&AtomIdent),
- {
- let attr = match self.get_part_attr() {
- Some(c) => c,
- None => return,
- };
-
- snapshot_helpers::each_class_or_part(attr, callback)
- }
-
- #[inline]
- fn has_snapshot(&self) -> bool {
- self.flags() & ELEMENT_HAS_SNAPSHOT != 0
- }
-
- #[inline]
- fn handled_snapshot(&self) -> bool {
- self.flags() & ELEMENT_HANDLED_SNAPSHOT != 0
- }
-
- unsafe fn set_handled_snapshot(&self) {
- debug_assert!(self.has_data());
- self.set_flags(ELEMENT_HANDLED_SNAPSHOT)
- }
-
- #[inline]
- fn has_dirty_descendants(&self) -> bool {
- self.flags() & ELEMENT_HAS_DIRTY_DESCENDANTS_FOR_SERVO != 0
- }
-
- unsafe fn set_dirty_descendants(&self) {
- debug_assert!(self.has_data());
- debug!("Setting dirty descendants: {:?}", self);
- self.set_flags(ELEMENT_HAS_DIRTY_DESCENDANTS_FOR_SERVO)
- }
-
- unsafe fn unset_dirty_descendants(&self) {
- self.unset_flags(ELEMENT_HAS_DIRTY_DESCENDANTS_FOR_SERVO)
- }
-
- #[inline]
- fn has_animation_only_dirty_descendants(&self) -> bool {
- self.flags() & ELEMENT_HAS_ANIMATION_ONLY_DIRTY_DESCENDANTS_FOR_SERVO != 0
- }
-
- unsafe fn set_animation_only_dirty_descendants(&self) {
- self.set_flags(ELEMENT_HAS_ANIMATION_ONLY_DIRTY_DESCENDANTS_FOR_SERVO)
- }
-
- unsafe fn unset_animation_only_dirty_descendants(&self) {
- self.unset_flags(ELEMENT_HAS_ANIMATION_ONLY_DIRTY_DESCENDANTS_FOR_SERVO)
- }
-
- unsafe fn clear_descendant_bits(&self) {
- self.unset_flags(
- ELEMENT_HAS_DIRTY_DESCENDANTS_FOR_SERVO |
- ELEMENT_HAS_ANIMATION_ONLY_DIRTY_DESCENDANTS_FOR_SERVO |
- NODE_DESCENDANTS_NEED_FRAMES,
- )
- }
-
- fn is_visited_link(&self) -> bool {
- self.state().intersects(ElementState::VISITED)
- }
-
- /// We want to match rules from the same tree in all cases, except for native anonymous content
- /// that _isn't_ part directly of a UA widget (e.g., such generated by form controls, or
- /// pseudo-elements).
- #[inline]
- fn matches_user_and_content_rules(&self) -> bool {
- use crate::gecko_bindings::structs::{
- NODE_HAS_BEEN_IN_UA_WIDGET, NODE_IS_IN_NATIVE_ANONYMOUS_SUBTREE,
- };
- let flags = self.flags();
- (flags & NODE_IS_IN_NATIVE_ANONYMOUS_SUBTREE) == 0 ||
- (flags & NODE_HAS_BEEN_IN_UA_WIDGET) != 0
- }
-
- #[inline]
- fn implemented_pseudo_element(&self) -> Option<PseudoElement> {
- if self.matches_user_and_content_rules() {
- return None;
- }
-
- if !self.has_properties() {
- return None;
- }
-
- PseudoElement::from_pseudo_type(unsafe { bindings::Gecko_GetImplementedPseudo(self.0) })
- }
-
- #[inline]
- fn store_children_to_process(&self, _: isize) {
- // This is only used for bottom-up traversal, and is thus a no-op for Gecko.
- }
-
- fn did_process_child(&self) -> isize {
- panic!("Atomic child count not implemented in Gecko");
- }
-
- unsafe fn ensure_data(&self) -> AtomicRefMut<ElementData> {
- if !self.has_data() {
- debug!("Creating ElementData for {:?}", self);
- let ptr = Box::into_raw(Box::new(AtomicRefCell::new(ElementData::default())));
- self.0.mServoData.set(ptr);
- }
- self.mutate_data().unwrap()
- }
-
- unsafe fn clear_data(&self) {
- let ptr = self.0.mServoData.get();
- self.unset_flags(
- ELEMENT_HAS_SNAPSHOT |
- ELEMENT_HANDLED_SNAPSHOT |
- structs::Element_kAllServoDescendantBits |
- NODE_NEEDS_FRAME,
- );
- if !ptr.is_null() {
- debug!("Dropping ElementData for {:?}", self);
- let data = Box::from_raw(self.0.mServoData.get());
- self.0.mServoData.set(ptr::null_mut());
-
- // Perform a mutable borrow of the data in debug builds. This
- // serves as an assertion that there are no outstanding borrows
- // when we destroy the data.
- debug_assert!({
- let _ = data.borrow_mut();
- true
- });
- }
- }
-
- #[inline]
- fn skip_item_display_fixup(&self) -> bool {
- debug_assert!(
- !self.is_pseudo_element(),
- "Just don't call me if I'm a pseudo, you should know the answer already"
- );
- self.is_root_of_native_anonymous_subtree()
- }
-
- #[inline]
- fn may_have_animations(&self) -> bool {
- if let Some(pseudo) = self.implemented_pseudo_element() {
- if pseudo.animations_stored_in_parent() {
- // FIXME(emilio): When would the parent of a ::before / ::after
- // pseudo-element be null?
- return self.parent_element().map_or(false, |p| {
- p.as_node()
- .get_bool_flag(nsINode_BooleanFlag::ElementHasAnimations)
- });
- }
- }
- self.as_node()
- .get_bool_flag(nsINode_BooleanFlag::ElementHasAnimations)
- }
-
- /// Process various tasks that are a result of animation-only restyle.
- fn process_post_animation(&self, tasks: PostAnimationTasks) {
- debug_assert!(!tasks.is_empty(), "Should be involved a task");
-
- // If display style was changed from none to other, we need to resolve
- // the descendants in the display:none subtree. Instead of resolving
- // those styles in animation-only restyle, we defer it to a subsequent
- // normal restyle.
- if tasks.intersects(PostAnimationTasks::DISPLAY_CHANGED_FROM_NONE_FOR_SMIL) {
- debug_assert!(
- self.implemented_pseudo_element()
- .map_or(true, |p| !p.is_before_or_after()),
- "display property animation shouldn't run on pseudo elements \
- since it's only for SMIL"
- );
- unsafe {
- self.note_explicit_hints(
- RestyleHint::restyle_subtree(),
- nsChangeHint::nsChangeHint_Empty,
- );
- }
- }
- }
-
- /// Update various animation-related state on a given (pseudo-)element as
- /// results of normal restyle.
- fn update_animations(
- &self,
- before_change_style: Option<Arc<ComputedValues>>,
- tasks: UpdateAnimationsTasks,
- ) {
- // We have to update animations even if the element has no computed
- // style since it means the element is in a display:none subtree, we
- // should destroy all CSS animations in display:none subtree.
- let computed_data = self.borrow_data();
- let computed_values = computed_data.as_ref().map(|d| d.styles.primary());
- let before_change_values = before_change_style
- .as_ref()
- .map_or(ptr::null(), |x| x.as_gecko_computed_style());
- let computed_values_opt = computed_values
- .as_ref()
- .map_or(ptr::null(), |x| x.as_gecko_computed_style());
- unsafe {
- Gecko_UpdateAnimations(
- self.0,
- before_change_values,
- computed_values_opt,
- tasks.bits(),
- );
- }
- }
-
- #[inline]
- fn has_animations(&self, _: &SharedStyleContext) -> bool {
- self.has_any_animation()
- }
-
- fn has_css_animations(&self, _: &SharedStyleContext, _: Option<PseudoElement>) -> bool {
- self.may_have_animations() && unsafe { Gecko_ElementHasCSSAnimations(self.0) }
- }
-
- fn has_css_transitions(&self, _: &SharedStyleContext, _: Option<PseudoElement>) -> bool {
- self.may_have_animations() && unsafe { Gecko_ElementHasCSSTransitions(self.0) }
- }
-
- // Detect if there are any changes that require us to update transitions.
- //
- // This is used as a more thoroughgoing check than the cheaper
- // might_need_transitions_update check.
- //
- // The following logic shadows the logic used on the Gecko side
- // (nsTransitionManager::DoUpdateTransitions) where we actually perform the
- // update.
- //
- // https://drafts.csswg.org/css-transitions/#starting
- fn needs_transitions_update(
- &self,
- before_change_style: &ComputedValues,
- after_change_style: &ComputedValues,
- ) -> bool {
- use crate::properties::LonghandIdSet;
-
- let after_change_ui_style = after_change_style.get_ui();
- let existing_transitions = self.css_transitions_info();
-
- if after_change_style.get_box().clone_display().is_none() {
- // We need to cancel existing transitions.
- return !existing_transitions.is_empty();
- }
-
- let mut transitions_to_keep = LonghandIdSet::new();
- for transition_property in after_change_style.transition_properties() {
- let physical_longhand = transition_property
- .longhand_id
- .to_physical(after_change_style.writing_mode);
- transitions_to_keep.insert(physical_longhand);
- if self.needs_transitions_update_per_property(
- physical_longhand,
- after_change_ui_style
- .transition_combined_duration_at(transition_property.index)
- .seconds(),
- before_change_style,
- after_change_style,
- &existing_transitions,
- ) {
- return true;
- }
- }
-
- // Check if we have to cancel the running transition because this is not
- // a matching transition-property value.
- existing_transitions
- .keys()
- .any(|property| !transitions_to_keep.contains(*property))
- }
-
- /// Whether there is an ElementData container.
- #[inline]
- fn has_data(&self) -> bool {
- self.get_data().is_some()
- }
-
- /// Immutably borrows the ElementData.
- fn borrow_data(&self) -> Option<AtomicRef<ElementData>> {
- self.get_data().map(|x| x.borrow())
- }
-
- /// Mutably borrows the ElementData.
- fn mutate_data(&self) -> Option<AtomicRefMut<ElementData>> {
- self.get_data().map(|x| x.borrow_mut())
- }
-
- #[inline]
- fn lang_attr(&self) -> Option<AttrValue> {
- let ptr = unsafe { bindings::Gecko_LangValue(self.0) };
- if ptr.is_null() {
- None
- } else {
- Some(AtomString(unsafe { Atom::from_addrefed(ptr) }))
- }
- }
-
- fn match_element_lang(&self, override_lang: Option<Option<AttrValue>>, value: &Lang) -> bool {
- // Gecko supports :lang() from CSS Selectors 4, which accepts a list
- // of language tags, and does BCP47-style range matching.
- let override_lang_ptr = match override_lang {
- Some(Some(ref atom)) => atom.as_ptr(),
- _ => ptr::null_mut(),
- };
- value.0.iter().any(|lang| unsafe {
- Gecko_MatchLang(
- self.0,
- override_lang_ptr,
- override_lang.is_some(),
- lang.as_slice().as_ptr(),
- )
- })
- }
-
- fn is_html_document_body_element(&self) -> bool {
- if self.local_name() != &**local_name!("body") {
- return false;
- }
-
- if !self.is_html_element() {
- return false;
- }
-
- unsafe { bindings::Gecko_IsDocumentBody(self.0) }
- }
-
- fn synthesize_presentational_hints_for_legacy_attributes<V>(
- &self,
- visited_handling: VisitedHandlingMode,
- hints: &mut V,
- ) where
- V: Push<ApplicableDeclarationBlock>,
- {
- use crate::properties::longhands::_x_lang::SpecifiedValue as SpecifiedLang;
- use crate::properties::longhands::color::SpecifiedValue as SpecifiedColor;
- use crate::stylesheets::layer_rule::LayerOrder;
- use crate::values::specified::{color::Color, font::XTextScale};
- lazy_static! {
- static ref TABLE_COLOR_RULE: ApplicableDeclarationBlock = {
- let global_style_data = &*GLOBAL_STYLE_DATA;
- let pdb = PropertyDeclarationBlock::with_one(
- PropertyDeclaration::Color(SpecifiedColor(Color::InheritFromBodyQuirk.into())),
- Importance::Normal,
- );
- let arc = Arc::new_leaked(global_style_data.shared_lock.wrap(pdb));
- ApplicableDeclarationBlock::from_declarations(
- arc,
- ServoCascadeLevel::PresHints,
- LayerOrder::root(),
- )
- };
- static ref MATHML_LANG_RULE: ApplicableDeclarationBlock = {
- let global_style_data = &*GLOBAL_STYLE_DATA;
- let pdb = PropertyDeclarationBlock::with_one(
- PropertyDeclaration::XLang(SpecifiedLang(atom!("x-math"))),
- Importance::Normal,
- );
- let arc = Arc::new_leaked(global_style_data.shared_lock.wrap(pdb));
- ApplicableDeclarationBlock::from_declarations(
- arc,
- ServoCascadeLevel::PresHints,
- LayerOrder::root(),
- )
- };
- static ref SVG_TEXT_DISABLE_SCALE_RULE: ApplicableDeclarationBlock = {
- let global_style_data = &*GLOBAL_STYLE_DATA;
- let pdb = PropertyDeclarationBlock::with_one(
- PropertyDeclaration::XTextScale(XTextScale::None),
- Importance::Normal,
- );
- let arc = Arc::new_leaked(global_style_data.shared_lock.wrap(pdb));
- ApplicableDeclarationBlock::from_declarations(
- arc,
- ServoCascadeLevel::PresHints,
- LayerOrder::root(),
- )
- };
- };
-
- let ns = self.namespace_id();
- // <th> elements get a default MozCenterOrInherit which may get overridden
- if ns == structs::kNameSpaceID_XHTML as i32 {
- if self.local_name().as_ptr() == atom!("table").as_ptr() &&
- self.as_node().owner_doc().quirks_mode() == QuirksMode::Quirks
- {
- hints.push(TABLE_COLOR_RULE.clone());
- }
- }
- if ns == structs::kNameSpaceID_SVG as i32 {
- if self.local_name().as_ptr() == atom!("text").as_ptr() {
- hints.push(SVG_TEXT_DISABLE_SCALE_RULE.clone());
- }
- }
- let declarations =
- unsafe { Gecko_GetHTMLPresentationAttrDeclarationBlock(self.0).as_ref() };
- if let Some(decl) = declarations {
- hints.push(ApplicableDeclarationBlock::from_declarations(
- unsafe { Arc::from_raw_addrefed(decl) },
- ServoCascadeLevel::PresHints,
- LayerOrder::root(),
- ));
- }
- let declarations = unsafe { Gecko_GetExtraContentStyleDeclarations(self.0).as_ref() };
- if let Some(decl) = declarations {
- hints.push(ApplicableDeclarationBlock::from_declarations(
- unsafe { Arc::from_raw_addrefed(decl) },
- ServoCascadeLevel::PresHints,
- LayerOrder::root(),
- ));
- }
-
- // Support for link, vlink, and alink presentation hints on <body>
- if self.is_link() {
- // Unvisited vs. visited styles are computed up-front based on the
- // visited mode (not the element's actual state).
- let declarations = match visited_handling {
- VisitedHandlingMode::AllLinksVisitedAndUnvisited => {
- unreachable!(
- "We should never try to selector match with \
- AllLinksVisitedAndUnvisited"
- );
- },
- VisitedHandlingMode::AllLinksUnvisited => unsafe {
- Gecko_GetUnvisitedLinkAttrDeclarationBlock(self.0).as_ref()
- },
- VisitedHandlingMode::RelevantLinkVisited => unsafe {
- Gecko_GetVisitedLinkAttrDeclarationBlock(self.0).as_ref()
- },
- };
- if let Some(decl) = declarations {
- hints.push(ApplicableDeclarationBlock::from_declarations(
- unsafe { Arc::from_raw_addrefed(decl) },
- ServoCascadeLevel::PresHints,
- LayerOrder::root(),
- ));
- }
-
- let active = self
- .state()
- .intersects(NonTSPseudoClass::Active.state_flag());
- if active {
- let declarations =
- unsafe { Gecko_GetActiveLinkAttrDeclarationBlock(self.0).as_ref() };
- if let Some(decl) = declarations {
- hints.push(ApplicableDeclarationBlock::from_declarations(
- unsafe { Arc::from_raw_addrefed(decl) },
- ServoCascadeLevel::PresHints,
- LayerOrder::root(),
- ));
- }
- }
- }
-
- // xml:lang has precedence over lang, which can be
- // set by Gecko_GetHTMLPresentationAttrDeclarationBlock
- //
- // http://www.whatwg.org/specs/web-apps/current-work/multipage/elements.html#language
- let ptr = unsafe { bindings::Gecko_GetXMLLangValue(self.0) };
- if !ptr.is_null() {
- let global_style_data = &*GLOBAL_STYLE_DATA;
-
- let pdb = PropertyDeclarationBlock::with_one(
- PropertyDeclaration::XLang(SpecifiedLang(unsafe { Atom::from_addrefed(ptr) })),
- Importance::Normal,
- );
- let arc = Arc::new(global_style_data.shared_lock.wrap(pdb));
- hints.push(ApplicableDeclarationBlock::from_declarations(
- arc,
- ServoCascadeLevel::PresHints,
- LayerOrder::root(),
- ))
- }
- // MathML's default lang has precedence over both `lang` and `xml:lang`
- if ns == structs::kNameSpaceID_MathML as i32 {
- if self.local_name().as_ptr() == atom!("math").as_ptr() {
- hints.push(MATHML_LANG_RULE.clone());
- }
- }
- }
-}
-
-impl<'le> PartialEq for GeckoElement<'le> {
- #[inline]
- fn eq(&self, other: &Self) -> bool {
- self.0 as *const _ == other.0 as *const _
- }
-}
-
-impl<'le> Eq for GeckoElement<'le> {}
-
-impl<'le> Hash for GeckoElement<'le> {
- #[inline]
- fn hash<H: Hasher>(&self, state: &mut H) {
- (self.0 as *const RawGeckoElement).hash(state);
- }
-}
-
-impl<'le> ::selectors::Element for GeckoElement<'le> {
- type Impl = SelectorImpl;
-
- #[inline]
- fn opaque(&self) -> OpaqueElement {
- OpaqueElement::new(self.0)
- }
-
- #[inline]
- fn parent_element(&self) -> Option<Self> {
- let parent_node = self.as_node().parent_node();
- parent_node.and_then(|n| n.as_element())
- }
-
- #[inline]
- fn parent_node_is_shadow_root(&self) -> bool {
- self.as_node()
- .parent_node()
- .map_or(false, |p| p.is_shadow_root())
- }
-
- #[inline]
- fn containing_shadow_host(&self) -> Option<Self> {
- let shadow = self.containing_shadow()?;
- Some(shadow.host())
- }
-
- #[inline]
- fn is_pseudo_element(&self) -> bool {
- self.implemented_pseudo_element().is_some()
- }
-
- #[inline]
- fn pseudo_element_originating_element(&self) -> Option<Self> {
- debug_assert!(self.is_pseudo_element());
- debug_assert!(!self.matches_user_and_content_rules());
- let mut current = *self;
- loop {
- if current.is_root_of_native_anonymous_subtree() {
- return current.traversal_parent();
- }
-
- current = current.traversal_parent()?;
- }
- }
-
- #[inline]
- fn assigned_slot(&self) -> Option<Self> {
- let slot = self.extended_slots()?._base.mAssignedSlot.mRawPtr;
-
- unsafe { Some(GeckoElement(&slot.as_ref()?._base._base._base._base)) }
- }
-
- #[inline]
- fn prev_sibling_element(&self) -> Option<Self> {
- let mut sibling = self.as_node().prev_sibling();
- while let Some(sibling_node) = sibling {
- if let Some(el) = sibling_node.as_element() {
- return Some(el);
- }
- sibling = sibling_node.prev_sibling();
- }
- None
- }
-
- #[inline]
- fn next_sibling_element(&self) -> Option<Self> {
- let mut sibling = self.as_node().next_sibling();
- while let Some(sibling_node) = sibling {
- if let Some(el) = sibling_node.as_element() {
- return Some(el);
- }
- sibling = sibling_node.next_sibling();
- }
- None
- }
-
- #[inline]
- fn first_element_child(&self) -> Option<Self> {
- let mut child = self.as_node().first_child();
- while let Some(child_node) = child {
- if let Some(el) = child_node.as_element() {
- return Some(el);
- }
- child = child_node.next_sibling();
- }
- None
- }
-
- fn apply_selector_flags(&self, flags: ElementSelectorFlags) {
- // Handle flags that apply to the element.
- let self_flags = flags.for_self();
- if !self_flags.is_empty() {
- self.set_flags(selector_flags_to_node_flags(flags))
- }
-
- // Handle flags that apply to the parent.
- let parent_flags = flags.for_parent();
- if !parent_flags.is_empty() {
- if let Some(p) = self.as_node().parent_node() {
- if p.is_element() || p.is_shadow_root() {
- p.set_flags(selector_flags_to_node_flags(parent_flags));
- }
- }
- }
- }
-
- fn has_attr_in_no_namespace(&self, local_name: &LocalName) -> bool {
- for attr in self.iter_attrs() {
- if attr.mName.mBits == local_name.as_ptr() as usize {
- return true;
- }
- }
- false
- }
-
- fn attr_matches(
- &self,
- ns: &NamespaceConstraint<&Namespace>,
- local_name: &LocalName,
- operation: &AttrSelectorOperation<&AttrValue>,
- ) -> bool {
- if self.attrs().is_none() {
- return false;
- }
- snapshot_helpers::attr_matches(self.iter_attrs(), ns, local_name, operation)
- }
-
- #[inline]
- fn is_root(&self) -> bool {
- if self
- .as_node()
- .get_bool_flag(nsINode_BooleanFlag::ParentIsContent)
- {
- return false;
- }
-
- if !self.as_node().is_in_document() {
- return false;
- }
-
- debug_assert!(self
- .as_node()
- .parent_node()
- .map_or(false, |p| p.is_document()));
- // XXX this should always return true at this point, shouldn't it?
- unsafe { bindings::Gecko_IsRootElement(self.0) }
- }
-
- fn is_empty(&self) -> bool {
- !self
- .as_node()
- .dom_children()
- .any(|child| unsafe { Gecko_IsSignificantChild(child.0, true) })
- }
-
- #[inline]
- fn has_local_name(&self, name: &WeakAtom) -> bool {
- self.local_name() == name
- }
-
- #[inline]
- fn has_namespace(&self, ns: &WeakNamespace) -> bool {
- self.namespace() == ns
- }
-
- #[inline]
- fn is_same_type(&self, other: &Self) -> bool {
- self.local_name() == other.local_name() && self.namespace() == other.namespace()
- }
-
- fn match_non_ts_pseudo_class(
- &self,
- pseudo_class: &NonTSPseudoClass,
- context: &mut MatchingContext<Self::Impl>,
- ) -> bool {
- use selectors::matching::*;
- match *pseudo_class {
- NonTSPseudoClass::Autofill |
- NonTSPseudoClass::Defined |
- NonTSPseudoClass::Focus |
- NonTSPseudoClass::Enabled |
- NonTSPseudoClass::Disabled |
- NonTSPseudoClass::Checked |
- NonTSPseudoClass::Fullscreen |
- NonTSPseudoClass::Indeterminate |
- NonTSPseudoClass::MozInert |
- NonTSPseudoClass::PopoverOpen |
- NonTSPseudoClass::PlaceholderShown |
- NonTSPseudoClass::Target |
- NonTSPseudoClass::Valid |
- NonTSPseudoClass::Invalid |
- NonTSPseudoClass::MozBroken |
- NonTSPseudoClass::MozLoading |
- NonTSPseudoClass::Required |
- NonTSPseudoClass::Optional |
- NonTSPseudoClass::ReadOnly |
- NonTSPseudoClass::ReadWrite |
- NonTSPseudoClass::FocusWithin |
- NonTSPseudoClass::FocusVisible |
- NonTSPseudoClass::MozDragOver |
- NonTSPseudoClass::MozDevtoolsHighlighted |
- NonTSPseudoClass::MozStyleeditorTransitioning |
- NonTSPseudoClass::MozMathIncrementScriptLevel |
- NonTSPseudoClass::InRange |
- NonTSPseudoClass::OutOfRange |
- NonTSPseudoClass::Default |
- NonTSPseudoClass::UserValid |
- NonTSPseudoClass::UserInvalid |
- NonTSPseudoClass::MozMeterOptimum |
- NonTSPseudoClass::MozMeterSubOptimum |
- NonTSPseudoClass::MozMeterSubSubOptimum |
- NonTSPseudoClass::MozHasDirAttr |
- NonTSPseudoClass::MozDirAttrLTR |
- NonTSPseudoClass::MozDirAttrRTL |
- NonTSPseudoClass::MozDirAttrLikeAuto |
- NonTSPseudoClass::Modal |
- NonTSPseudoClass::MozTopmostModal |
- NonTSPseudoClass::Active |
- NonTSPseudoClass::Hover |
- NonTSPseudoClass::MozAutofillPreview |
- NonTSPseudoClass::MozRevealed |
- NonTSPseudoClass::MozValueEmpty |
- NonTSPseudoClass::Dir(..) => self.state().intersects(pseudo_class.state_flag()),
- NonTSPseudoClass::AnyLink => self.is_link(),
- NonTSPseudoClass::Link => {
- self.is_link() && context.visited_handling().matches_unvisited()
- },
- NonTSPseudoClass::Visited => {
- self.is_link() && context.visited_handling().matches_visited()
- },
- NonTSPseudoClass::MozFirstNode => {
- if context.needs_selector_flags() {
- self.apply_selector_flags(ElementSelectorFlags::HAS_EDGE_CHILD_SELECTOR);
- }
- let mut elem = self.as_node();
- while let Some(prev) = elem.prev_sibling() {
- if prev.contains_non_whitespace_content() {
- return false;
- }
- elem = prev;
- }
- true
- },
- NonTSPseudoClass::MozLastNode => {
- if context.needs_selector_flags() {
- self.apply_selector_flags(ElementSelectorFlags::HAS_EDGE_CHILD_SELECTOR);
- }
- let mut elem = self.as_node();
- while let Some(next) = elem.next_sibling() {
- if next.contains_non_whitespace_content() {
- return false;
- }
- elem = next;
- }
- true
- },
- NonTSPseudoClass::MozOnlyWhitespace => {
- if context.needs_selector_flags() {
- self.apply_selector_flags(ElementSelectorFlags::HAS_EMPTY_SELECTOR);
- }
- if self
- .as_node()
- .dom_children()
- .any(|c| c.contains_non_whitespace_content())
- {
- return false;
- }
- true
- },
- NonTSPseudoClass::MozNativeAnonymous => !self.matches_user_and_content_rules(),
- NonTSPseudoClass::MozUseShadowTreeRoot => self.is_root_of_use_element_shadow_tree(),
- NonTSPseudoClass::MozTableBorderNonzero => unsafe {
- bindings::Gecko_IsTableBorderNonzero(self.0)
- },
- NonTSPseudoClass::MozBrowserFrame => unsafe { bindings::Gecko_IsBrowserFrame(self.0) },
- NonTSPseudoClass::MozSelectListBox => unsafe {
- bindings::Gecko_IsSelectListBox(self.0)
- },
- NonTSPseudoClass::MozIsHTML => self.is_html_element_in_html_document(),
-
- NonTSPseudoClass::MozLWTheme |
- NonTSPseudoClass::MozLocaleDir(..) |
- NonTSPseudoClass::MozWindowInactive => {
- let state_bit = pseudo_class.document_state_flag();
- if state_bit.is_empty() {
- debug_assert!(
- matches!(pseudo_class, NonTSPseudoClass::MozLocaleDir(..)),
- "Only moz-locale-dir should ever return an empty state"
- );
- return false;
- }
- if context
- .extra_data
- .invalidation_data
- .document_state
- .intersects(state_bit)
- {
- return !context.in_negation();
- }
- self.document_state().contains(state_bit)
- },
- NonTSPseudoClass::MozPlaceholder => false,
- NonTSPseudoClass::Lang(ref lang_arg) => self.match_element_lang(None, lang_arg),
- }
- }
-
- fn match_pseudo_element(
- &self,
- pseudo_element: &PseudoElement,
- _context: &mut MatchingContext<Self::Impl>,
- ) -> bool {
- // TODO(emilio): I believe we could assert we are a pseudo-element and
- // match the proper pseudo-element, given how we rulehash the stuff
- // based on the pseudo.
- match self.implemented_pseudo_element() {
- Some(ref pseudo) => *pseudo == *pseudo_element,
- None => false,
- }
- }
-
- #[inline]
- fn is_link(&self) -> bool {
- self.state().intersects(ElementState::VISITED_OR_UNVISITED)
- }
-
- #[inline]
- fn has_id(&self, id: &AtomIdent, case_sensitivity: CaseSensitivity) -> bool {
- if !self.has_id() {
- return false;
- }
-
- let element_id = match snapshot_helpers::get_id(self.non_mapped_attrs()) {
- Some(id) => id,
- None => return false,
- };
-
- case_sensitivity.eq_atom(element_id, id)
- }
-
- #[inline]
- fn is_part(&self, name: &AtomIdent) -> bool {
- let attr = match self.get_part_attr() {
- Some(c) => c,
- None => return false,
- };
-
- snapshot_helpers::has_class_or_part(name, CaseSensitivity::CaseSensitive, attr)
- }
-
- #[inline]
- fn imported_part(&self, name: &AtomIdent) -> Option<AtomIdent> {
- snapshot_helpers::imported_part(self.non_mapped_attrs(), name)
- }
-
- #[inline(always)]
- fn has_class(&self, name: &AtomIdent, case_sensitivity: CaseSensitivity) -> bool {
- let attr = match self.get_class_attr() {
- Some(c) => c,
- None => return false,
- };
-
- snapshot_helpers::has_class_or_part(name, case_sensitivity, attr)
- }
-
- #[inline]
- fn is_html_element_in_html_document(&self) -> bool {
- self.is_html_element() && self.as_node().owner_doc().is_html_document()
- }
-
- #[inline]
- fn is_html_slot_element(&self) -> bool {
- self.is_html_element() && self.local_name().as_ptr() == local_name!("slot").as_ptr()
- }
-
- #[inline]
- fn ignores_nth_child_selectors(&self) -> bool {
- self.is_root_of_native_anonymous_subtree()
- }
-}
diff --git a/components/style/gecko_bindings/mod.rs b/components/style/gecko_bindings/mod.rs
deleted file mode 100644
index f0b0adc7ecb..00000000000
--- a/components/style/gecko_bindings/mod.rs
+++ /dev/null
@@ -1,28 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
-
-//! Gecko's C++ bindings, along with some rust helpers to ease its use.
-
-// FIXME: We allow `improper_ctypes` (for now), because the lint doesn't allow
-// foreign structs to have `PhantomData`. We should remove this once the lint
-// ignores this case.
-
-#[allow(
- dead_code,
- improper_ctypes,
- non_camel_case_types,
- non_snake_case,
- non_upper_case_globals,
- missing_docs
-)]
-// TODO: Remove this when updating bindgen, see
-// https://github.com/rust-lang/rust-bindgen/issues/1651
-#[cfg_attr(test, allow(deref_nullptr))]
-pub mod structs {
- include!(concat!(env!("OUT_DIR"), "/gecko/structs.rs"));
-}
-
-pub use self::structs as bindings;
-
-pub mod sugar;
diff --git a/components/style/gecko_bindings/sugar/mod.rs b/components/style/gecko_bindings/sugar/mod.rs
deleted file mode 100644
index 00faf63ba66..00000000000
--- a/components/style/gecko_bindings/sugar/mod.rs
+++ /dev/null
@@ -1,13 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
-
-//! Rust sugar and convenience methods for Gecko types.
-
-mod ns_com_ptr;
-mod ns_compatibility;
-mod ns_style_auto_array;
-mod ns_t_array;
-pub mod origin_flags;
-pub mod ownership;
-pub mod refptr;
diff --git a/components/style/gecko_bindings/sugar/ns_com_ptr.rs b/components/style/gecko_bindings/sugar/ns_com_ptr.rs
deleted file mode 100644
index 1c54541bd80..00000000000
--- a/components/style/gecko_bindings/sugar/ns_com_ptr.rs
+++ /dev/null
@@ -1,15 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
-
-//! Little helpers for `nsCOMPtr`.
-
-use crate::gecko_bindings::structs::nsCOMPtr;
-
-impl<T> nsCOMPtr<T> {
- /// Get this pointer as a raw pointer.
- #[inline]
- pub fn raw(&self) -> *mut T {
- self.mRawPtr
- }
-}
diff --git a/components/style/gecko_bindings/sugar/ns_compatibility.rs b/components/style/gecko_bindings/sugar/ns_compatibility.rs
deleted file mode 100644
index f4b81e9f790..00000000000
--- a/components/style/gecko_bindings/sugar/ns_compatibility.rs
+++ /dev/null
@@ -1,19 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
-
-//! Little helper for `nsCompatibility`.
-
-use crate::context::QuirksMode;
-use crate::gecko_bindings::structs::nsCompatibility;
-
-impl From<nsCompatibility> for QuirksMode {
- #[inline]
- fn from(mode: nsCompatibility) -> QuirksMode {
- match mode {
- nsCompatibility::eCompatibility_FullStandards => QuirksMode::NoQuirks,
- nsCompatibility::eCompatibility_AlmostStandards => QuirksMode::LimitedQuirks,
- nsCompatibility::eCompatibility_NavQuirks => QuirksMode::Quirks,
- }
- }
-}
diff --git a/components/style/gecko_bindings/sugar/ns_style_auto_array.rs b/components/style/gecko_bindings/sugar/ns_style_auto_array.rs
deleted file mode 100644
index b5772a6c77a..00000000000
--- a/components/style/gecko_bindings/sugar/ns_style_auto_array.rs
+++ /dev/null
@@ -1,111 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
-
-//! Rust helpers for Gecko's `nsStyleAutoArray`.
-
-use crate::gecko_bindings::bindings::Gecko_EnsureStyleAnimationArrayLength;
-use crate::gecko_bindings::bindings::Gecko_EnsureStyleScrollTimelineArrayLength;
-use crate::gecko_bindings::bindings::Gecko_EnsureStyleTransitionArrayLength;
-use crate::gecko_bindings::bindings::Gecko_EnsureStyleViewTimelineArrayLength;
-use crate::gecko_bindings::structs::nsStyleAutoArray;
-use crate::gecko_bindings::structs::{StyleAnimation, StyleTransition};
-use crate::gecko_bindings::structs::{StyleScrollTimeline, StyleViewTimeline};
-use std::iter::{once, Chain, IntoIterator, Once};
-use std::ops::{Index, IndexMut};
-use std::slice::{Iter, IterMut};
-
-impl<T> Index<usize> for nsStyleAutoArray<T> {
- type Output = T;
- fn index(&self, index: usize) -> &T {
- match index {
- 0 => &self.mFirstElement,
- _ => &self.mOtherElements[index - 1],
- }
- }
-}
-
-impl<T> IndexMut<usize> for nsStyleAutoArray<T> {
- fn index_mut(&mut self, index: usize) -> &mut T {
- match index {
- 0 => &mut self.mFirstElement,
- _ => &mut self.mOtherElements[index - 1],
- }
- }
-}
-
-impl<T> nsStyleAutoArray<T> {
- /// Mutably iterate over the array elements.
- pub fn iter_mut(&mut self) -> Chain<Once<&mut T>, IterMut<T>> {
- once(&mut self.mFirstElement).chain(self.mOtherElements.iter_mut())
- }
-
- /// Iterate over the array elements.
- pub fn iter(&self) -> Chain<Once<&T>, Iter<T>> {
- once(&self.mFirstElement).chain(self.mOtherElements.iter())
- }
-
- /// Returns the length of the array.
- ///
- /// Note that often structs containing autoarrays will have additional
- /// member fields that contain the length, which must be kept in sync.
- pub fn len(&self) -> usize {
- 1 + self.mOtherElements.len()
- }
-}
-
-impl nsStyleAutoArray<StyleAnimation> {
- /// Ensures that the array has length at least the given length.
- pub fn ensure_len(&mut self, len: usize) {
- unsafe {
- Gecko_EnsureStyleAnimationArrayLength(
- self as *mut nsStyleAutoArray<StyleAnimation> as *mut _,
- len,
- );
- }
- }
-}
-
-impl nsStyleAutoArray<StyleTransition> {
- /// Ensures that the array has length at least the given length.
- pub fn ensure_len(&mut self, len: usize) {
- unsafe {
- Gecko_EnsureStyleTransitionArrayLength(
- self as *mut nsStyleAutoArray<StyleTransition> as *mut _,
- len,
- );
- }
- }
-}
-
-impl nsStyleAutoArray<StyleViewTimeline> {
- /// Ensures that the array has length at least the given length.
- pub fn ensure_len(&mut self, len: usize) {
- unsafe {
- Gecko_EnsureStyleViewTimelineArrayLength(
- self as *mut nsStyleAutoArray<StyleViewTimeline> as *mut _,
- len,
- );
- }
- }
-}
-
-impl nsStyleAutoArray<StyleScrollTimeline> {
- /// Ensures that the array has length at least the given length.
- pub fn ensure_len(&mut self, len: usize) {
- unsafe {
- Gecko_EnsureStyleScrollTimelineArrayLength(
- self as *mut nsStyleAutoArray<StyleScrollTimeline> as *mut _,
- len,
- );
- }
- }
-}
-
-impl<'a, T> IntoIterator for &'a mut nsStyleAutoArray<T> {
- type Item = &'a mut T;
- type IntoIter = Chain<Once<&'a mut T>, IterMut<'a, T>>;
- fn into_iter(self) -> Self::IntoIter {
- self.iter_mut()
- }
-}
diff --git a/components/style/gecko_bindings/sugar/ns_t_array.rs b/components/style/gecko_bindings/sugar/ns_t_array.rs
deleted file mode 100644
index d10ed420dd8..00000000000
--- a/components/style/gecko_bindings/sugar/ns_t_array.rs
+++ /dev/null
@@ -1,144 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
-
-//! Rust helpers for Gecko's nsTArray.
-
-use crate::gecko_bindings::bindings;
-use crate::gecko_bindings::structs::{nsTArray, nsTArrayHeader, CopyableTArray};
-use std::mem;
-use std::ops::{Deref, DerefMut};
-use std::slice;
-
-impl<T> Deref for nsTArray<T> {
- type Target = [T];
-
- #[inline]
- fn deref<'a>(&'a self) -> &'a [T] {
- unsafe { slice::from_raw_parts(self.slice_begin(), self.header().mLength as usize) }
- }
-}
-
-impl<T> DerefMut for nsTArray<T> {
- fn deref_mut<'a>(&'a mut self) -> &'a mut [T] {
- unsafe { slice::from_raw_parts_mut(self.slice_begin(), self.header().mLength as usize) }
- }
-}
-
-impl<T> nsTArray<T> {
- #[inline]
- fn header<'a>(&'a self) -> &'a nsTArrayHeader {
- debug_assert!(!self.mBuffer.is_null());
- unsafe { mem::transmute(self.mBuffer) }
- }
-
- // unsafe, since header may be in shared static or something
- unsafe fn header_mut<'a>(&'a mut self) -> &'a mut nsTArrayHeader {
- debug_assert!(!self.mBuffer.is_null());
-
- mem::transmute(self.mBuffer)
- }
-
- #[inline]
- unsafe fn slice_begin(&self) -> *mut T {
- debug_assert!(!self.mBuffer.is_null());
- (self.mBuffer as *const nsTArrayHeader).offset(1) as *mut _
- }
-
- /// Ensures the array has enough capacity at least to hold `cap` elements.
- ///
- /// NOTE: This doesn't call the constructor on the values!
- pub fn ensure_capacity(&mut self, cap: usize) {
- if cap >= self.len() {
- unsafe {
- bindings::Gecko_EnsureTArrayCapacity(
- self as *mut nsTArray<T> as *mut _,
- cap,
- mem::size_of::<T>(),
- )
- }
- }
- }
-
- /// Clears the array storage without calling the destructor on the values.
- #[inline]
- pub unsafe fn clear(&mut self) {
- if self.len() != 0 {
- bindings::Gecko_ClearPODTArray(
- self as *mut nsTArray<T> as *mut _,
- mem::size_of::<T>(),
- mem::align_of::<T>(),
- );
- }
- }
-
- /// Clears a POD array. This is safe since copy types are memcopyable.
- #[inline]
- pub fn clear_pod(&mut self)
- where
- T: Copy,
- {
- unsafe { self.clear() }
- }
-
- /// Resize and set the length of the array to `len`.
- ///
- /// unsafe because this may leave the array with uninitialized elements.
- ///
- /// This will not call constructors. If you need that, either manually add
- /// bindings or run the typed `EnsureCapacity` call on the gecko side.
- pub unsafe fn set_len(&mut self, len: u32) {
- // this can leak
- debug_assert!(len >= self.len() as u32);
- if self.len() == len as usize {
- return;
- }
- self.ensure_capacity(len as usize);
- self.header_mut().mLength = len;
- }
-
- /// Resizes an array containing only POD elements
- ///
- /// unsafe because this may leave the array with uninitialized elements.
- ///
- /// This will not leak since it only works on POD types (and thus doesn't assert)
- pub unsafe fn set_len_pod(&mut self, len: u32)
- where
- T: Copy,
- {
- if self.len() == len as usize {
- return;
- }
- self.ensure_capacity(len as usize);
- let header = self.header_mut();
- header.mLength = len;
- }
-
- /// Collects the given iterator into this array.
- ///
- /// Not unsafe because we won't leave uninitialized elements in the array.
- pub fn assign_from_iter_pod<I>(&mut self, iter: I)
- where
- T: Copy,
- I: ExactSizeIterator + Iterator<Item = T>,
- {
- debug_assert!(iter.len() <= 0xFFFFFFFF);
- unsafe {
- self.set_len_pod(iter.len() as u32);
- }
- self.iter_mut().zip(iter).for_each(|(r, v)| *r = v);
- }
-}
-
-impl<T> Deref for CopyableTArray<T> {
- type Target = nsTArray<T>;
- fn deref(&self) -> &Self::Target {
- &self._base
- }
-}
-
-impl<T> DerefMut for CopyableTArray<T> {
- fn deref_mut(&mut self) -> &mut nsTArray<T> {
- &mut self._base
- }
-}
diff --git a/components/style/gecko_bindings/sugar/origin_flags.rs b/components/style/gecko_bindings/sugar/origin_flags.rs
deleted file mode 100644
index e0f0981c5d8..00000000000
--- a/components/style/gecko_bindings/sugar/origin_flags.rs
+++ /dev/null
@@ -1,31 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
-
-//! Helper to iterate over `OriginFlags` bits.
-
-use crate::gecko_bindings::structs::OriginFlags;
-use crate::stylesheets::OriginSet;
-
-/// Checks that the values for OriginFlags are the ones we expect.
-pub fn assert_flags_match() {
- use crate::stylesheets::origin::*;
- debug_assert_eq!(
- OriginFlags::UserAgent.0,
- OriginSet::ORIGIN_USER_AGENT.bits()
- );
- debug_assert_eq!(OriginFlags::Author.0, OriginSet::ORIGIN_AUTHOR.bits());
- debug_assert_eq!(OriginFlags::User.0, OriginSet::ORIGIN_USER.bits());
-}
-
-impl From<OriginFlags> for OriginSet {
- fn from(flags: OriginFlags) -> Self {
- Self::from_bits_truncate(flags.0)
- }
-}
-
-impl From<OriginSet> for OriginFlags {
- fn from(set: OriginSet) -> Self {
- OriginFlags(set.bits())
- }
-}
diff --git a/components/style/gecko_bindings/sugar/ownership.rs b/components/style/gecko_bindings/sugar/ownership.rs
deleted file mode 100644
index 31b512cf1e8..00000000000
--- a/components/style/gecko_bindings/sugar/ownership.rs
+++ /dev/null
@@ -1,61 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
-
-//! Helpers for different FFI pointer kinds that Gecko's FFI layer uses.
-
-use crate::gecko_bindings::structs::root::mozilla::detail::CopyablePtr;
-use servo_arc::Arc;
-use std::marker::PhantomData;
-use std::ops::{Deref, DerefMut};
-use std::ptr;
-
-/// Gecko-FFI-safe Arc (T is an ArcInner).
-///
-/// This can be null.
-///
-/// Leaks on drop. Please don't drop this.
-#[repr(C)]
-pub struct Strong<GeckoType> {
- ptr: *const GeckoType,
- _marker: PhantomData<GeckoType>,
-}
-
-impl<T> From<Arc<T>> for Strong<T> {
- fn from(arc: Arc<T>) -> Self {
- Self {
- ptr: Arc::into_raw(arc),
- _marker: PhantomData,
- }
- }
-}
-
-impl<GeckoType> Strong<GeckoType> {
- #[inline]
- /// Returns whether this reference is null.
- pub fn is_null(&self) -> bool {
- self.ptr.is_null()
- }
-
- #[inline]
- /// Returns a null pointer
- pub fn null() -> Self {
- Self {
- ptr: ptr::null(),
- _marker: PhantomData,
- }
- }
-}
-
-impl<T> Deref for CopyablePtr<T> {
- type Target = T;
- fn deref(&self) -> &Self::Target {
- &self.mPtr
- }
-}
-
-impl<T> DerefMut for CopyablePtr<T> {
- fn deref_mut<'a>(&'a mut self) -> &'a mut T {
- &mut self.mPtr
- }
-}
diff --git a/components/style/gecko_bindings/sugar/refptr.rs b/components/style/gecko_bindings/sugar/refptr.rs
deleted file mode 100644
index c4a0479a077..00000000000
--- a/components/style/gecko_bindings/sugar/refptr.rs
+++ /dev/null
@@ -1,289 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
-
-//! A rust helper to ease the use of Gecko's refcounted types.
-
-use crate::gecko_bindings::{bindings, structs};
-use crate::Atom;
-use servo_arc::Arc;
-use std::fmt::Write;
-use std::marker::PhantomData;
-use std::ops::Deref;
-use std::{fmt, mem, ptr};
-
-/// Trait for all objects that have Addref() and Release
-/// methods and can be placed inside RefPtr<T>
-pub unsafe trait RefCounted {
- /// Bump the reference count.
- fn addref(&self);
- /// Decrease the reference count.
- unsafe fn release(&self);
-}
-
-/// Trait for types which can be shared across threads in RefPtr.
-pub unsafe trait ThreadSafeRefCounted: RefCounted {}
-
-/// A custom RefPtr implementation to take into account Drop semantics and
-/// a bit less-painful memory management.
-pub struct RefPtr<T: RefCounted> {
- ptr: *mut T,
- _marker: PhantomData<T>,
-}
-
-impl<T: RefCounted> fmt::Debug for RefPtr<T> {
- fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
- f.write_str("RefPtr { ")?;
- self.ptr.fmt(f)?;
- f.write_char('}')
- }
-}
-
-impl<T: RefCounted> RefPtr<T> {
- /// Create a new RefPtr from an already addrefed pointer obtained from FFI.
- ///
- /// The pointer must be valid, non-null and have been addrefed.
- pub unsafe fn from_addrefed(ptr: *mut T) -> Self {
- debug_assert!(!ptr.is_null());
- RefPtr {
- ptr,
- _marker: PhantomData,
- }
- }
-
- /// Returns whether the current pointer is null.
- pub fn is_null(&self) -> bool {
- self.ptr.is_null()
- }
-
- /// Returns a null pointer.
- pub fn null() -> Self {
- Self {
- ptr: ptr::null_mut(),
- _marker: PhantomData,
- }
- }
-
- /// Create a new RefPtr from a pointer obtained from FFI.
- ///
- /// This method calls addref() internally
- pub unsafe fn new(ptr: *mut T) -> Self {
- let ret = RefPtr {
- ptr,
- _marker: PhantomData,
- };
- ret.addref();
- ret
- }
-
- /// Produces an FFI-compatible RefPtr that can be stored in style structs.
- ///
- /// structs::RefPtr does not have a destructor, so this may leak
- pub fn forget(self) -> structs::RefPtr<T> {
- let ret = structs::RefPtr {
- mRawPtr: self.ptr,
- _phantom_0: PhantomData,
- };
- mem::forget(self);
- ret
- }
-
- /// Returns the raw inner pointer to be fed back into FFI.
- pub fn get(&self) -> *mut T {
- self.ptr
- }
-
- /// Addref the inner data, obviously leaky on its own.
- pub fn addref(&self) {
- if !self.ptr.is_null() {
- unsafe {
- (*self.ptr).addref();
- }
- }
- }
-
- /// Release the inner data.
- ///
- /// Call only when the data actually needs releasing.
- pub unsafe fn release(&self) {
- if !self.ptr.is_null() {
- (*self.ptr).release();
- }
- }
-}
-
-impl<T: RefCounted> Deref for RefPtr<T> {
- type Target = T;
- fn deref(&self) -> &T {
- debug_assert!(!self.ptr.is_null());
- unsafe { &*self.ptr }
- }
-}
-
-impl<T: RefCounted> structs::RefPtr<T> {
- /// Produces a Rust-side RefPtr from an FFI RefPtr, bumping the refcount.
- ///
- /// Must be called on a valid, non-null structs::RefPtr<T>.
- pub unsafe fn to_safe(&self) -> RefPtr<T> {
- let r = RefPtr {
- ptr: self.mRawPtr,
- _marker: PhantomData,
- };
- r.addref();
- r
- }
- /// Produces a Rust-side RefPtr, consuming the existing one (and not bumping
- /// the refcount).
- pub unsafe fn into_safe(self) -> RefPtr<T> {
- debug_assert!(!self.mRawPtr.is_null());
- RefPtr {
- ptr: self.mRawPtr,
- _marker: PhantomData,
- }
- }
-
- /// Replace a structs::RefPtr<T> with a different one, appropriately
- /// addref/releasing.
- ///
- /// Both `self` and `other` must be valid, but can be null.
- ///
- /// Safe when called on an aliased pointer because the refcount in that case
- /// needs to be at least two.
- pub unsafe fn set(&mut self, other: &Self) {
- self.clear();
- if !other.mRawPtr.is_null() {
- *self = other.to_safe().forget();
- }
- }
-
- /// Clear an instance of the structs::RefPtr<T>, by releasing
- /// it and setting its contents to null.
- ///
- /// `self` must be valid, but can be null.
- pub unsafe fn clear(&mut self) {
- if !self.mRawPtr.is_null() {
- (*self.mRawPtr).release();
- self.mRawPtr = ptr::null_mut();
- }
- }
-
- /// Replace a `structs::RefPtr<T>` with a `RefPtr<T>`,
- /// consuming the `RefPtr<T>`, and releasing the old
- /// value in `self` if necessary.
- ///
- /// `self` must be valid, possibly null.
- pub fn set_move(&mut self, other: RefPtr<T>) {
- if !self.mRawPtr.is_null() {
- unsafe {
- (*self.mRawPtr).release();
- }
- }
- *self = other.forget();
- }
-}
-
-impl<T> structs::RefPtr<T> {
- /// Returns a new, null refptr.
- pub fn null() -> Self {
- Self {
- mRawPtr: ptr::null_mut(),
- _phantom_0: PhantomData,
- }
- }
-
- /// Create a new RefPtr from an arc.
- pub fn from_arc(s: Arc<T>) -> Self {
- Self {
- mRawPtr: Arc::into_raw(s) as *mut _,
- _phantom_0: PhantomData,
- }
- }
-
- /// Sets the contents to an Arc<T>.
- pub fn set_arc(&mut self, other: Arc<T>) {
- unsafe {
- if !self.mRawPtr.is_null() {
- let _ = Arc::from_raw(self.mRawPtr);
- }
- self.mRawPtr = Arc::into_raw(other) as *mut _;
- }
- }
-}
-
-impl<T: RefCounted> Drop for RefPtr<T> {
- fn drop(&mut self) {
- unsafe { self.release() }
- }
-}
-
-impl<T: RefCounted> Clone for RefPtr<T> {
- fn clone(&self) -> Self {
- self.addref();
- RefPtr {
- ptr: self.ptr,
- _marker: PhantomData,
- }
- }
-}
-
-impl<T: RefCounted> PartialEq for RefPtr<T> {
- fn eq(&self, other: &Self) -> bool {
- self.ptr == other.ptr
- }
-}
-
-unsafe impl<T: ThreadSafeRefCounted> Send for RefPtr<T> {}
-unsafe impl<T: ThreadSafeRefCounted> Sync for RefPtr<T> {}
-
-macro_rules! impl_refcount {
- ($t:ty, $addref:path, $release:path) => {
- unsafe impl RefCounted for $t {
- #[inline]
- fn addref(&self) {
- unsafe { $addref(self as *const _ as *mut _) }
- }
-
- #[inline]
- unsafe fn release(&self) {
- $release(self as *const _ as *mut _)
- }
- }
- };
-}
-
-// Companion of NS_DECL_THREADSAFE_FFI_REFCOUNTING.
-//
-// Gets you a free RefCounted impl implemented via FFI.
-macro_rules! impl_threadsafe_refcount {
- ($t:ty, $addref:path, $release:path) => {
- impl_refcount!($t, $addref, $release);
- unsafe impl ThreadSafeRefCounted for $t {}
- };
-}
-
-impl_threadsafe_refcount!(
- structs::mozilla::URLExtraData,
- bindings::Gecko_AddRefURLExtraDataArbitraryThread,
- bindings::Gecko_ReleaseURLExtraDataArbitraryThread
-);
-impl_threadsafe_refcount!(
- structs::nsIURI,
- bindings::Gecko_AddRefnsIURIArbitraryThread,
- bindings::Gecko_ReleasensIURIArbitraryThread
-);
-impl_threadsafe_refcount!(
- structs::SheetLoadDataHolder,
- bindings::Gecko_AddRefSheetLoadDataHolderArbitraryThread,
- bindings::Gecko_ReleaseSheetLoadDataHolderArbitraryThread
-);
-
-#[inline]
-unsafe fn addref_atom(atom: *mut structs::nsAtom) {
- mem::forget(Atom::from_raw(atom));
-}
-
-#[inline]
-unsafe fn release_atom(atom: *mut structs::nsAtom) {
- let _ = Atom::from_addrefed(atom);
-}
-impl_threadsafe_refcount!(structs::nsAtom, addref_atom, release_atom);
diff --git a/components/style/gecko_string_cache/mod.rs b/components/style/gecko_string_cache/mod.rs
deleted file mode 100644
index cb040390cfe..00000000000
--- a/components/style/gecko_string_cache/mod.rs
+++ /dev/null
@@ -1,532 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
-
-#![allow(unsafe_code)]
-// This is needed for the constants in atom_macro.rs, because we have some
-// atoms whose names differ only by case, e.g. datetime and dateTime.
-#![allow(non_upper_case_globals)]
-
-//! A drop-in replacement for string_cache, but backed by Gecko `nsAtom`s.
-
-use crate::gecko_bindings::bindings::Gecko_AddRefAtom;
-use crate::gecko_bindings::bindings::Gecko_Atomize;
-use crate::gecko_bindings::bindings::Gecko_Atomize16;
-use crate::gecko_bindings::bindings::Gecko_ReleaseAtom;
-use crate::gecko_bindings::structs::root::mozilla::detail::gGkAtoms;
-use crate::gecko_bindings::structs::root::mozilla::detail::kGkAtomsArrayOffset;
-use crate::gecko_bindings::structs::root::mozilla::detail::GkAtoms_Atoms_AtomsCount;
-use crate::gecko_bindings::structs::{nsAtom, nsDynamicAtom, nsStaticAtom};
-use nsstring::{nsAString, nsStr};
-use precomputed_hash::PrecomputedHash;
-use std::borrow::{Borrow, Cow};
-use std::char::{self, DecodeUtf16};
-use std::fmt::{self, Write};
-use std::hash::{Hash, Hasher};
-use std::iter::Cloned;
-use std::mem::{self, ManuallyDrop};
-use std::num::NonZeroUsize;
-use std::ops::Deref;
-use std::{slice, str};
-use style_traits::SpecifiedValueInfo;
-use to_shmem::{self, SharedMemoryBuilder, ToShmem};
-
-#[macro_use]
-#[allow(improper_ctypes, non_camel_case_types, missing_docs)]
-pub mod atom_macro {
- include!(concat!(env!("OUT_DIR"), "/gecko/atom_macro.rs"));
-}
-
-#[macro_use]
-pub mod namespace;
-
-pub use self::namespace::{Namespace, WeakNamespace};
-
-/// A handle to a Gecko atom. This is a type that can represent either:
-///
-/// * A strong reference to a dynamic atom (an `nsAtom` pointer), in which case
-/// the `usize` just holds the pointer value.
-///
-/// * A byte offset from `gGkAtoms` to the `nsStaticAtom` object (shifted to
-/// the left one bit, and with the lower bit set to `1` to differentiate it
-/// from the above), so `(offset << 1 | 1)`.
-///
-#[derive(Eq, PartialEq)]
-#[repr(C)]
-pub struct Atom(NonZeroUsize);
-
-/// An atom *without* a strong reference.
-///
-/// Only usable as `&'a WeakAtom`,
-/// where `'a` is the lifetime of something that holds a strong reference to that atom.
-pub struct WeakAtom(nsAtom);
-
-/// The number of static atoms we have.
-const STATIC_ATOM_COUNT: usize = GkAtoms_Atoms_AtomsCount as usize;
-
-/// Returns the Gecko static atom array.
-///
-/// We have this rather than use rust-bindgen to generate
-/// mozilla::detail::gGkAtoms and then just reference gGkAtoms.mAtoms, so we
-/// avoid a problem with lld-link.exe on Windows.
-///
-/// https://bugzilla.mozilla.org/show_bug.cgi?id=1517685
-#[inline]
-fn static_atoms() -> &'static [nsStaticAtom; STATIC_ATOM_COUNT] {
- unsafe {
- let addr = &gGkAtoms as *const _ as usize + kGkAtomsArrayOffset as usize;
- &*(addr as *const _)
- }
-}
-
-/// Returns whether the specified address points to one of the nsStaticAtom
-/// objects in the Gecko static atom array.
-#[inline]
-fn valid_static_atom_addr(addr: usize) -> bool {
- unsafe {
- let atoms = static_atoms();
- let start = atoms.as_ptr();
- let end = atoms.get_unchecked(STATIC_ATOM_COUNT) as *const _;
- let in_range = addr >= start as usize && addr < end as usize;
- let aligned = addr % mem::align_of::<nsStaticAtom>() == 0;
- in_range && aligned
- }
-}
-
-impl Deref for Atom {
- type Target = WeakAtom;
-
- #[inline]
- fn deref(&self) -> &WeakAtom {
- unsafe {
- let addr = if self.is_static() {
- (&gGkAtoms as *const _ as usize) + (self.0.get() >> 1)
- } else {
- self.0.get()
- };
- debug_assert!(!self.is_static() || valid_static_atom_addr(addr));
- WeakAtom::new(addr as *const nsAtom)
- }
- }
-}
-
-impl PrecomputedHash for Atom {
- #[inline]
- fn precomputed_hash(&self) -> u32 {
- self.get_hash()
- }
-}
-
-impl Borrow<WeakAtom> for Atom {
- #[inline]
- fn borrow(&self) -> &WeakAtom {
- self
- }
-}
-
-impl ToShmem for Atom {
- fn to_shmem(&self, _builder: &mut SharedMemoryBuilder) -> to_shmem::Result<Self> {
- if !self.is_static() {
- return Err(format!(
- "ToShmem failed for Atom: must be a static atom: {}",
- self
- ));
- }
-
- Ok(ManuallyDrop::new(Atom(self.0)))
- }
-}
-
-impl Eq for WeakAtom {}
-impl PartialEq for WeakAtom {
- #[inline]
- fn eq(&self, other: &Self) -> bool {
- let weak: *const WeakAtom = self;
- let other: *const WeakAtom = other;
- weak == other
- }
-}
-
-impl PartialEq<Atom> for WeakAtom {
- #[inline]
- fn eq(&self, other: &Atom) -> bool {
- self == &**other
- }
-}
-
-unsafe impl Send for Atom {}
-unsafe impl Sync for Atom {}
-unsafe impl Sync for WeakAtom {}
-
-impl WeakAtom {
- /// Construct a `WeakAtom` from a raw `nsAtom`.
- #[inline]
- pub unsafe fn new<'a>(atom: *const nsAtom) -> &'a mut Self {
- &mut *(atom as *mut WeakAtom)
- }
-
- /// Clone this atom, bumping the refcount if the atom is not static.
- #[inline]
- pub fn clone(&self) -> Atom {
- unsafe { Atom::from_raw(self.as_ptr()) }
- }
-
- /// Get the atom hash.
- #[inline]
- pub fn get_hash(&self) -> u32 {
- self.0.mHash
- }
-
- /// Get the atom as a slice of utf-16 chars.
- #[inline]
- pub fn as_slice(&self) -> &[u16] {
- let string = if self.is_static() {
- let atom_ptr = self.as_ptr() as *const nsStaticAtom;
- let string_offset = unsafe { (*atom_ptr).mStringOffset };
- let string_offset = -(string_offset as isize);
- let u8_ptr = atom_ptr as *const u8;
- // It is safe to use offset() here because both addresses are within
- // the same struct, e.g. mozilla::detail::gGkAtoms.
- unsafe { u8_ptr.offset(string_offset) as *const u16 }
- } else {
- let atom_ptr = self.as_ptr() as *const nsDynamicAtom;
- // Dynamic atom chars are stored at the end of the object.
- unsafe { atom_ptr.offset(1) as *const u16 }
- };
- unsafe { slice::from_raw_parts(string, self.len() as usize) }
- }
-
- // NOTE: don't expose this, since it's slow, and easy to be misused.
- fn chars(&self) -> DecodeUtf16<Cloned<slice::Iter<u16>>> {
- char::decode_utf16(self.as_slice().iter().cloned())
- }
-
- /// Execute `cb` with the string that this atom represents.
- ///
- /// Find alternatives to this function when possible, please, since it's
- /// pretty slow.
- pub fn with_str<F, Output>(&self, cb: F) -> Output
- where
- F: FnOnce(&str) -> Output,
- {
- let mut buffer = mem::MaybeUninit::<[u8; 64]>::uninit();
- let buffer = unsafe { &mut *buffer.as_mut_ptr() };
-
- // The total string length in utf16 is going to be less than or equal
- // the slice length (each utf16 character is going to take at least one
- // and at most 2 items in the utf16 slice).
- //
- // Each of those characters will take at most four bytes in the utf8
- // one. Thus if the slice is less than 64 / 4 (16) we can guarantee that
- // we'll decode it in place.
- let owned_string;
- let len = self.len();
- let utf8_slice = if len <= 16 {
- let mut total_len = 0;
-
- for c in self.chars() {
- let c = c.unwrap_or(char::REPLACEMENT_CHARACTER);
- let utf8_len = c.encode_utf8(&mut buffer[total_len..]).len();
- total_len += utf8_len;
- }
-
- let slice = unsafe { str::from_utf8_unchecked(&buffer[..total_len]) };
- debug_assert_eq!(slice, String::from_utf16_lossy(self.as_slice()));
- slice
- } else {
- owned_string = String::from_utf16_lossy(self.as_slice());
- &*owned_string
- };
-
- cb(utf8_slice)
- }
-
- /// Returns whether this atom is static.
- #[inline]
- pub fn is_static(&self) -> bool {
- self.0.mIsStatic() != 0
- }
-
- /// Returns whether this atom is ascii lowercase.
- #[inline]
- fn is_ascii_lowercase(&self) -> bool {
- self.0.mIsAsciiLowercase() != 0
- }
-
- /// Returns the length of the atom string.
- #[inline]
- pub fn len(&self) -> u32 {
- self.0.mLength()
- }
-
- /// Returns whether this atom is the empty string.
- #[inline]
- pub fn is_empty(&self) -> bool {
- self.len() == 0
- }
-
- /// Returns the atom as a mutable pointer.
- #[inline]
- pub fn as_ptr(&self) -> *mut nsAtom {
- let const_ptr: *const nsAtom = &self.0;
- const_ptr as *mut nsAtom
- }
-
- /// Convert this atom to ASCII lower-case
- pub fn to_ascii_lowercase(&self) -> Atom {
- if self.is_ascii_lowercase() {
- return self.clone();
- }
-
- let slice = self.as_slice();
- let mut buffer = mem::MaybeUninit::<[u16; 64]>::uninit();
- let buffer = unsafe { &mut *buffer.as_mut_ptr() };
- let mut vec;
- let mutable_slice = if let Some(buffer_prefix) = buffer.get_mut(..slice.len()) {
- buffer_prefix.copy_from_slice(slice);
- buffer_prefix
- } else {
- vec = slice.to_vec();
- &mut vec
- };
- for char16 in &mut *mutable_slice {
- if *char16 <= 0x7F {
- *char16 = (*char16 as u8).to_ascii_lowercase() as u16
- }
- }
- Atom::from(&*mutable_slice)
- }
-
- /// Return whether two atoms are ASCII-case-insensitive matches
- #[inline]
- pub fn eq_ignore_ascii_case(&self, other: &Self) -> bool {
- if self == other {
- return true;
- }
-
- // If we know both atoms are ascii-lowercase, then we can stick with
- // pointer equality.
- if self.is_ascii_lowercase() && other.is_ascii_lowercase() {
- debug_assert!(!self.eq_ignore_ascii_case_slow(other));
- return false;
- }
-
- self.eq_ignore_ascii_case_slow(other)
- }
-
- fn eq_ignore_ascii_case_slow(&self, other: &Self) -> bool {
- let a = self.as_slice();
- let b = other.as_slice();
-
- if a.len() != b.len() {
- return false;
- }
-
- a.iter().zip(b).all(|(&a16, &b16)| {
- if a16 <= 0x7F && b16 <= 0x7F {
- (a16 as u8).eq_ignore_ascii_case(&(b16 as u8))
- } else {
- a16 == b16
- }
- })
- }
-}
-
-impl fmt::Debug for WeakAtom {
- fn fmt(&self, w: &mut fmt::Formatter) -> fmt::Result {
- write!(w, "Gecko WeakAtom({:p}, {})", self, self)
- }
-}
-
-impl fmt::Display for WeakAtom {
- fn fmt(&self, w: &mut fmt::Formatter) -> fmt::Result {
- for c in self.chars() {
- w.write_char(c.unwrap_or(char::REPLACEMENT_CHARACTER))?
- }
- Ok(())
- }
-}
-
-#[inline]
-unsafe fn make_handle(ptr: *const nsAtom) -> NonZeroUsize {
- debug_assert!(!ptr.is_null());
- if !WeakAtom::new(ptr).is_static() {
- NonZeroUsize::new_unchecked(ptr as usize)
- } else {
- make_static_handle(ptr as *mut nsStaticAtom)
- }
-}
-
-#[inline]
-unsafe fn make_static_handle(ptr: *const nsStaticAtom) -> NonZeroUsize {
- // FIXME(heycam): Use offset_from once it's stabilized.
- // https://github.com/rust-lang/rust/issues/41079
- debug_assert!(valid_static_atom_addr(ptr as usize));
- let base = &gGkAtoms as *const _;
- let offset = ptr as usize - base as usize;
- NonZeroUsize::new_unchecked((offset << 1) | 1)
-}
-
-impl Atom {
- #[inline]
- fn is_static(&self) -> bool {
- self.0.get() & 1 == 1
- }
-
- /// Execute a callback with the atom represented by `ptr`.
- pub unsafe fn with<F, R>(ptr: *const nsAtom, callback: F) -> R
- where
- F: FnOnce(&Atom) -> R,
- {
- let atom = Atom(make_handle(ptr as *mut nsAtom));
- let ret = callback(&atom);
- mem::forget(atom);
- ret
- }
-
- /// Creates a static atom from its index in the static atom table, without
- /// checking.
- #[inline]
- pub const unsafe fn from_index_unchecked(index: u16) -> Self {
- // FIXME(emilio): No support for debug_assert! in const fn for now. Note
- // that violating this invariant will debug-assert in the `Deref` impl
- // though.
- //
- // debug_assert!((index as usize) < STATIC_ATOM_COUNT);
- let offset =
- (index as usize) * std::mem::size_of::<nsStaticAtom>() + kGkAtomsArrayOffset as usize;
- Atom(NonZeroUsize::new_unchecked((offset << 1) | 1))
- }
-
- /// Creates an atom from an atom pointer.
- #[inline(always)]
- pub unsafe fn from_raw(ptr: *mut nsAtom) -> Self {
- let atom = Atom(make_handle(ptr));
- if !atom.is_static() {
- Gecko_AddRefAtom(ptr);
- }
- atom
- }
-
- /// Creates an atom from an atom pointer that has already had AddRef
- /// called on it. This may be a static or dynamic atom.
- #[inline]
- pub unsafe fn from_addrefed(ptr: *mut nsAtom) -> Self {
- assert!(!ptr.is_null());
- Atom(make_handle(ptr))
- }
-
- /// Convert this atom into an addrefed nsAtom pointer.
- #[inline]
- pub fn into_addrefed(self) -> *mut nsAtom {
- let ptr = self.as_ptr();
- mem::forget(self);
- ptr
- }
-}
-
-impl Hash for Atom {
- fn hash<H>(&self, state: &mut H)
- where
- H: Hasher,
- {
- state.write_u32(self.get_hash());
- }
-}
-
-impl Hash for WeakAtom {
- fn hash<H>(&self, state: &mut H)
- where
- H: Hasher,
- {
- state.write_u32(self.get_hash());
- }
-}
-
-impl Clone for Atom {
- #[inline(always)]
- fn clone(&self) -> Atom {
- unsafe {
- let atom = Atom(self.0);
- if !atom.is_static() {
- Gecko_AddRefAtom(atom.as_ptr());
- }
- atom
- }
- }
-}
-
-impl Drop for Atom {
- #[inline]
- fn drop(&mut self) {
- if !self.is_static() {
- unsafe {
- Gecko_ReleaseAtom(self.as_ptr());
- }
- }
- }
-}
-
-impl Default for Atom {
- #[inline]
- fn default() -> Self {
- atom!("")
- }
-}
-
-impl fmt::Debug for Atom {
- fn fmt(&self, w: &mut fmt::Formatter) -> fmt::Result {
- write!(w, "Atom(0x{:08x}, {})", self.0, self)
- }
-}
-
-impl fmt::Display for Atom {
- fn fmt(&self, w: &mut fmt::Formatter) -> fmt::Result {
- self.deref().fmt(w)
- }
-}
-
-impl<'a> From<&'a str> for Atom {
- #[inline]
- fn from(string: &str) -> Atom {
- debug_assert!(string.len() <= u32::max_value() as usize);
- unsafe {
- Atom::from_addrefed(Gecko_Atomize(
- string.as_ptr() as *const _,
- string.len() as u32,
- ))
- }
- }
-}
-
-impl<'a> From<&'a [u16]> for Atom {
- #[inline]
- fn from(slice: &[u16]) -> Atom {
- Atom::from(&*nsStr::from(slice))
- }
-}
-
-impl<'a> From<&'a nsAString> for Atom {
- #[inline]
- fn from(string: &nsAString) -> Atom {
- unsafe { Atom::from_addrefed(Gecko_Atomize16(string)) }
- }
-}
-
-impl<'a> From<Cow<'a, str>> for Atom {
- #[inline]
- fn from(string: Cow<'a, str>) -> Atom {
- Atom::from(&*string)
- }
-}
-
-impl From<String> for Atom {
- #[inline]
- fn from(string: String) -> Atom {
- Atom::from(&*string)
- }
-}
-
-malloc_size_of_is_0!(Atom);
-
-impl SpecifiedValueInfo for Atom {}
diff --git a/components/style/gecko_string_cache/namespace.rs b/components/style/gecko_string_cache/namespace.rs
deleted file mode 100644
index d9745b9e21f..00000000000
--- a/components/style/gecko_string_cache/namespace.rs
+++ /dev/null
@@ -1,105 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
-
-//! A type to represent a namespace.
-
-use crate::gecko_bindings::structs::nsAtom;
-use crate::string_cache::{Atom, WeakAtom};
-use precomputed_hash::PrecomputedHash;
-use std::borrow::Borrow;
-use std::fmt;
-use std::ops::Deref;
-
-/// In Gecko namespaces are just regular atoms, so this is a simple macro to
-/// forward one macro to the other.
-#[macro_export]
-macro_rules! ns {
- () => {
- $crate::string_cache::Namespace(atom!(""))
- };
- ($s:tt) => {
- $crate::string_cache::Namespace(atom!($s))
- };
-}
-
-/// A Gecko namespace is just a wrapped atom.
-#[derive(
- Clone,
- Debug,
- Default,
- Eq,
- Hash,
- MallocSizeOf,
- PartialEq,
- ToComputedValue,
- ToResolvedValue,
- ToShmem,
-)]
-#[repr(transparent)]
-pub struct Namespace(pub Atom);
-
-impl PrecomputedHash for Namespace {
- #[inline]
- fn precomputed_hash(&self) -> u32 {
- self.0.precomputed_hash()
- }
-}
-
-/// A Gecko WeakNamespace is a wrapped WeakAtom.
-#[derive(Deref, Hash)]
-pub struct WeakNamespace(WeakAtom);
-
-impl Deref for Namespace {
- type Target = WeakNamespace;
-
- #[inline]
- fn deref(&self) -> &WeakNamespace {
- let weak: *const WeakAtom = &*self.0;
- unsafe { &*(weak as *const WeakNamespace) }
- }
-}
-
-impl<'a> From<&'a str> for Namespace {
- fn from(s: &'a str) -> Self {
- Namespace(Atom::from(s))
- }
-}
-
-impl fmt::Display for Namespace {
- fn fmt(&self, w: &mut fmt::Formatter) -> fmt::Result {
- self.0.fmt(w)
- }
-}
-
-impl Borrow<WeakNamespace> for Namespace {
- #[inline]
- fn borrow(&self) -> &WeakNamespace {
- self
- }
-}
-
-impl WeakNamespace {
- /// Trivially construct a WeakNamespace.
- #[inline]
- pub unsafe fn new<'a>(atom: *mut nsAtom) -> &'a Self {
- &*(atom as *const WeakNamespace)
- }
-
- /// Clone this WeakNamespace to obtain a strong reference to the same
- /// underlying namespace.
- #[inline]
- pub fn clone(&self) -> Namespace {
- Namespace(self.0.clone())
- }
-}
-
-impl Eq for WeakNamespace {}
-impl PartialEq for WeakNamespace {
- #[inline]
- fn eq(&self, other: &Self) -> bool {
- let weak: *const WeakNamespace = self;
- let other: *const WeakNamespace = other;
- weak == other
- }
-}
diff --git a/components/style/global_style_data.rs b/components/style/global_style_data.rs
deleted file mode 100644
index 8b5f3c89024..00000000000
--- a/components/style/global_style_data.rs
+++ /dev/null
@@ -1,173 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
-
-//! Global style data
-
-use crate::context::StyleSystemOptions;
-#[cfg(feature = "gecko")]
-use crate::gecko_bindings::bindings;
-use crate::parallel::STYLE_THREAD_STACK_SIZE_KB;
-use crate::shared_lock::SharedRwLock;
-use crate::thread_state;
-#[cfg(feature = "gecko")]
-use gecko_profiler;
-use parking_lot::{Mutex, RwLock, RwLockReadGuard};
-use rayon;
-use std::{io, thread};
-
-/// Global style data
-pub struct GlobalStyleData {
- /// Shared RWLock for CSSOM objects
- pub shared_lock: SharedRwLock,
-
- /// Global style system options determined by env vars.
- pub options: StyleSystemOptions,
-}
-
-/// Global thread pool.
-pub struct StyleThreadPool {
- /// How many threads parallel styling can use. If not using a thread pool, this is set to `None`.
- pub num_threads: Option<usize>,
-
- /// The parallel styling thread pool.
- ///
- /// For leak-checking purposes, we want to terminate the thread-pool, which
- /// waits for all the async jobs to complete. Thus the RwLock.
- style_thread_pool: RwLock<Option<rayon::ThreadPool>>,
-}
-
-fn thread_name(index: usize) -> String {
- format!("Style#{}", index)
-}
-
-lazy_static! {
- /// JoinHandles for spawned style threads. These will be joined during
- /// StyleThreadPool::shutdown() after exiting the thread pool.
- ///
- /// This would be quite inefficient if rayon destroyed and re-created
- /// threads regularly during threadpool operation in response to demand,
- /// however rayon actually never destroys its threads until the entire
- /// thread pool is shut-down, so the size of this list is bounded.
- static ref STYLE_THREAD_JOIN_HANDLES: Mutex<Vec<thread::JoinHandle<()>>> =
- Mutex::new(Vec::new());
-}
-
-fn thread_spawn(options: rayon::ThreadBuilder) -> io::Result<()> {
- let mut b = thread::Builder::new();
- if let Some(name) = options.name() {
- b = b.name(name.to_owned());
- }
- if let Some(stack_size) = options.stack_size() {
- b = b.stack_size(stack_size);
- }
- let join_handle = b.spawn(|| options.run())?;
- STYLE_THREAD_JOIN_HANDLES.lock().push(join_handle);
- Ok(())
-}
-
-fn thread_startup(_index: usize) {
- thread_state::initialize_layout_worker_thread();
- #[cfg(feature = "gecko")]
- unsafe {
- bindings::Gecko_SetJemallocThreadLocalArena(true);
- let name = thread_name(_index);
- gecko_profiler::register_thread(&name);
- }
-}
-
-fn thread_shutdown(_: usize) {
- #[cfg(feature = "gecko")]
- unsafe {
- gecko_profiler::unregister_thread();
- bindings::Gecko_SetJemallocThreadLocalArena(false);
- }
-}
-
-impl StyleThreadPool {
- /// Shuts down the thread pool, waiting for all work to complete.
- pub fn shutdown() {
- if STYLE_THREAD_JOIN_HANDLES.lock().is_empty() {
- return;
- }
- {
- // Drop the pool.
- let _ = STYLE_THREAD_POOL.lock().unwrap().style_thread_pool.write().take();
- }
-
- // Join spawned threads until all of the threads have been joined. This
- // will usually be pretty fast, as on shutdown there should be basically
- // no threads left running.
- while let Some(join_handle) = STYLE_THREAD_JOIN_HANDLES.lock().pop() {
- let _ = join_handle.join();
- }
- }
-
- /// Returns a reference to the thread pool.
- ///
- /// We only really want to give read-only access to the pool, except
- /// for shutdown().
- pub fn pool(&self) -> RwLockReadGuard<Option<rayon::ThreadPool>> {
- self.style_thread_pool.read()
- }
-}
-
-#[cfg(feature = "servo")]
-fn stylo_threads_pref() -> i32 {
- style_config::get_i32("layout.threads")
-}
-
-#[cfg(feature = "gecko")]
-fn stylo_threads_pref() -> i32 {
- static_prefs::pref!("layout.css.stylo-threads")
-}
-
-/// The performance benefit of additional threads seems to level off at around six, so we cap it
-/// there on many-core machines (see bug 1431285 comment 14).
-pub(crate) const STYLO_MAX_THREADS: usize = 6;
-
-lazy_static! {
- /// Global thread pool
- pub static ref STYLE_THREAD_POOL: std::sync::Mutex<StyleThreadPool> = {
- use std::cmp;
- // We always set this pref on startup, before layout or script have had a chance of
- // accessing (and thus creating) the thread-pool.
- let threads_pref: i32 = stylo_threads_pref();
- let num_threads = if threads_pref >= 0 {
- threads_pref as usize
- } else {
- use num_cpus;
- // The default heuristic is num_virtual_cores * .75. This gives us three threads on a
- // hyper-threaded dual core, and six threads on a hyper-threaded quad core.
- let threads = cmp::max(num_cpus::get() * 3 / 4, 1);
- // There's no point in creating a thread pool if there's one thread.
- if threads == 1 { 0 } else { threads }
- };
-
- let num_threads = cmp::min(num_threads, STYLO_MAX_THREADS);
- let (pool, num_threads) = if num_threads < 1 {
- (None, None)
- } else {
- let workers = rayon::ThreadPoolBuilder::new()
- .spawn_handler(thread_spawn)
- .num_threads(num_threads)
- .thread_name(thread_name)
- .start_handler(thread_startup)
- .exit_handler(thread_shutdown)
- .stack_size(STYLE_THREAD_STACK_SIZE_KB * 1024)
- .build();
- (workers.ok(), Some(num_threads))
- };
-
- std::sync::Mutex::new(StyleThreadPool {
- num_threads,
- style_thread_pool: RwLock::new(pool),
- })
- };
-
- /// Global style data
- pub static ref GLOBAL_STYLE_DATA: GlobalStyleData = GlobalStyleData {
- shared_lock: SharedRwLock::new_leaked(),
- options: StyleSystemOptions::default(),
- };
-}
diff --git a/components/style/invalidation/element/document_state.rs b/components/style/invalidation/element/document_state.rs
deleted file mode 100644
index b0bbce3b855..00000000000
--- a/components/style/invalidation/element/document_state.rs
+++ /dev/null
@@ -1,142 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
-
-//! An invalidation processor for style changes due to document state changes.
-
-use crate::dom::TElement;
-use crate::invalidation::element::invalidation_map::Dependency;
-use crate::invalidation::element::invalidator::{DescendantInvalidationLists, InvalidationVector};
-use crate::invalidation::element::invalidator::{Invalidation, InvalidationProcessor};
-use crate::invalidation::element::state_and_attributes;
-use crate::stylist::CascadeData;
-use selectors::matching::{
- MatchingContext, MatchingMode, NeedsSelectorFlags, QuirksMode, VisitedHandlingMode,
-};
-use selectors::NthIndexCache;
-use style_traits::dom::DocumentState;
-
-/// A struct holding the members necessary to invalidate document state
-/// selectors.
-#[derive(Debug)]
-pub struct InvalidationMatchingData {
- /// The document state that has changed, which makes it always match.
- pub document_state: DocumentState,
-}
-
-impl Default for InvalidationMatchingData {
- #[inline(always)]
- fn default() -> Self {
- Self {
- document_state: DocumentState::empty(),
- }
- }
-}
-
-/// An invalidation processor for style changes due to state and attribute
-/// changes.
-pub struct DocumentStateInvalidationProcessor<'a, E: TElement, I> {
- rules: I,
- matching_context: MatchingContext<'a, E::Impl>,
- document_states_changed: DocumentState,
-}
-
-impl<'a, E: TElement, I> DocumentStateInvalidationProcessor<'a, E, I> {
- /// Creates a new DocumentStateInvalidationProcessor.
- #[inline]
- pub fn new(
- rules: I,
- document_states_changed: DocumentState,
- nth_index_cache: &'a mut NthIndexCache,
- quirks_mode: QuirksMode,
- ) -> Self {
- let mut matching_context = MatchingContext::<'a, E::Impl>::new_for_visited(
- MatchingMode::Normal,
- None,
- nth_index_cache,
- VisitedHandlingMode::AllLinksVisitedAndUnvisited,
- quirks_mode,
- NeedsSelectorFlags::No,
- );
-
- matching_context.extra_data.invalidation_data.document_state = document_states_changed;
-
- Self {
- rules,
- document_states_changed,
- matching_context,
- }
- }
-}
-
-impl<'a, E, I> InvalidationProcessor<'a, E> for DocumentStateInvalidationProcessor<'a, E, I>
-where
- E: TElement,
- I: Iterator<Item = &'a CascadeData>,
-{
- fn check_outer_dependency(&mut self, _: &Dependency, _: E) -> bool {
- debug_assert!(
- false,
- "how, we should only have parent-less dependencies here!"
- );
- true
- }
-
- fn collect_invalidations(
- &mut self,
- _element: E,
- self_invalidations: &mut InvalidationVector<'a>,
- _descendant_invalidations: &mut DescendantInvalidationLists<'a>,
- _sibling_invalidations: &mut InvalidationVector<'a>,
- ) -> bool {
- for cascade_data in &mut self.rules {
- let map = cascade_data.invalidation_map();
- for dependency in &map.document_state_selectors {
- if !dependency.state.intersects(self.document_states_changed) {
- continue;
- }
-
- // We pass `None` as a scope, as document state selectors aren't
- // affected by the current scope.
- //
- // FIXME(emilio): We should really pass the relevant host for
- // self.rules, so that we invalidate correctly if the selector
- // happens to have something like :host(:-moz-window-inactive)
- // for example.
- self_invalidations.push(Invalidation::new(
- &dependency.dependency,
- /* scope = */ None,
- ));
- }
- }
-
- false
- }
-
- fn matching_context(&mut self) -> &mut MatchingContext<'a, E::Impl> {
- &mut self.matching_context
- }
-
- fn recursion_limit_exceeded(&mut self, _: E) {
- unreachable!("We don't run document state invalidation with stack limits")
- }
-
- fn should_process_descendants(&mut self, element: E) -> bool {
- match element.borrow_data() {
- Some(d) => state_and_attributes::should_process_descendants(&d),
- None => false,
- }
- }
-
- fn invalidated_descendants(&mut self, element: E, child: E) {
- state_and_attributes::invalidated_descendants(element, child)
- }
-
- fn invalidated_self(&mut self, element: E) {
- state_and_attributes::invalidated_self(element);
- }
-
- fn invalidated_sibling(&mut self, sibling: E, of: E) {
- state_and_attributes::invalidated_sibling(sibling, of);
- }
-}
diff --git a/components/style/invalidation/element/element_wrapper.rs b/components/style/invalidation/element/element_wrapper.rs
deleted file mode 100644
index 8c25153d1a8..00000000000
--- a/components/style/invalidation/element/element_wrapper.rs
+++ /dev/null
@@ -1,391 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
-
-//! A wrapper over an element and a snapshot, that allows us to selector-match
-//! against a past state of the element.
-
-use crate::dom::TElement;
-use crate::selector_parser::{AttrValue, NonTSPseudoClass, PseudoElement, SelectorImpl};
-use crate::selector_parser::{Snapshot, SnapshotMap};
-use crate::values::AtomIdent;
-use crate::{CaseSensitivityExt, LocalName, Namespace, WeakAtom};
-use selectors::attr::{AttrSelectorOperation, CaseSensitivity, NamespaceConstraint};
-use selectors::matching::{ElementSelectorFlags, MatchingContext};
-use selectors::{Element, OpaqueElement};
-use std::cell::Cell;
-use std::fmt;
-use style_traits::dom::ElementState;
-
-/// In order to compute restyle hints, we perform a selector match against a
-/// list of partial selectors whose rightmost simple selector may be sensitive
-/// to the thing being changed. We do this matching twice, once for the element
-/// as it exists now and once for the element as it existed at the time of the
-/// last restyle. If the results of the selector match differ, that means that
-/// the given partial selector is sensitive to the change, and we compute a
-/// restyle hint based on its combinator.
-///
-/// In order to run selector matching against the old element state, we generate
-/// a wrapper for the element which claims to have the old state. This is the
-/// ElementWrapper logic below.
-///
-/// Gecko does this differently for element states, and passes a mask called
-/// mStateMask, which indicates the states that need to be ignored during
-/// selector matching. This saves an ElementWrapper allocation and an additional
-/// selector match call at the expense of additional complexity inside the
-/// selector matching logic. This only works for boolean states though, so we
-/// still need to take the ElementWrapper approach for attribute-dependent
-/// style. So we do it the same both ways for now to reduce complexity, but it's
-/// worth measuring the performance impact (if any) of the mStateMask approach.
-pub trait ElementSnapshot: Sized {
- /// The state of the snapshot, if any.
- fn state(&self) -> Option<ElementState>;
-
- /// If this snapshot contains attribute information.
- fn has_attrs(&self) -> bool;
-
- /// Gets the attribute information of the snapshot as a string.
- ///
- /// Only for debugging purposes.
- fn debug_list_attributes(&self) -> String {
- String::new()
- }
-
- /// The ID attribute per this snapshot. Should only be called if
- /// `has_attrs()` returns true.
- fn id_attr(&self) -> Option<&WeakAtom>;
-
- /// Whether this snapshot contains the class `name`. Should only be called
- /// if `has_attrs()` returns true.
- fn has_class(&self, name: &AtomIdent, case_sensitivity: CaseSensitivity) -> bool;
-
- /// Whether this snapshot represents the part named `name`. Should only be
- /// called if `has_attrs()` returns true.
- fn is_part(&self, name: &AtomIdent) -> bool;
-
- /// See Element::imported_part.
- fn imported_part(&self, name: &AtomIdent) -> Option<AtomIdent>;
-
- /// A callback that should be called for each class of the snapshot. Should
- /// only be called if `has_attrs()` returns true.
- fn each_class<F>(&self, _: F)
- where
- F: FnMut(&AtomIdent);
-
- /// The `xml:lang=""` or `lang=""` attribute value per this snapshot.
- fn lang_attr(&self) -> Option<AttrValue>;
-}
-
-/// A simple wrapper over an element and a snapshot, that allows us to
-/// selector-match against a past state of the element.
-#[derive(Clone)]
-pub struct ElementWrapper<'a, E>
-where
- E: TElement,
-{
- element: E,
- cached_snapshot: Cell<Option<&'a Snapshot>>,
- snapshot_map: &'a SnapshotMap,
-}
-
-impl<'a, E> ElementWrapper<'a, E>
-where
- E: TElement,
-{
- /// Trivially constructs an `ElementWrapper`.
- pub fn new(el: E, snapshot_map: &'a SnapshotMap) -> Self {
- ElementWrapper {
- element: el,
- cached_snapshot: Cell::new(None),
- snapshot_map: snapshot_map,
- }
- }
-
- /// Gets the snapshot associated with this element, if any.
- pub fn snapshot(&self) -> Option<&'a Snapshot> {
- if !self.element.has_snapshot() {
- return None;
- }
-
- if let Some(s) = self.cached_snapshot.get() {
- return Some(s);
- }
-
- let snapshot = self.snapshot_map.get(&self.element);
- debug_assert!(snapshot.is_some(), "has_snapshot lied!");
-
- self.cached_snapshot.set(snapshot);
-
- snapshot
- }
-
- /// Returns the states that have changed since the element was snapshotted.
- pub fn state_changes(&self) -> ElementState {
- let snapshot = match self.snapshot() {
- Some(s) => s,
- None => return ElementState::empty(),
- };
-
- match snapshot.state() {
- Some(state) => state ^ self.element.state(),
- None => ElementState::empty(),
- }
- }
-
- /// Returns the value of the `xml:lang=""` (or, if appropriate, `lang=""`)
- /// attribute from this element's snapshot or the closest ancestor
- /// element snapshot with the attribute specified.
- fn get_lang(&self) -> Option<AttrValue> {
- let mut current = self.clone();
- loop {
- let lang = match self.snapshot() {
- Some(snapshot) if snapshot.has_attrs() => snapshot.lang_attr(),
- _ => current.element.lang_attr(),
- };
- if lang.is_some() {
- return lang;
- }
- current = current.parent_element()?;
- }
- }
-}
-
-impl<'a, E> fmt::Debug for ElementWrapper<'a, E>
-where
- E: TElement,
-{
- fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
- // Ignore other fields for now, can change later if needed.
- self.element.fmt(f)
- }
-}
-
-impl<'a, E> Element for ElementWrapper<'a, E>
-where
- E: TElement,
-{
- type Impl = SelectorImpl;
-
- fn match_non_ts_pseudo_class(
- &self,
- pseudo_class: &NonTSPseudoClass,
- context: &mut MatchingContext<Self::Impl>,
- ) -> bool {
- // Some pseudo-classes need special handling to evaluate them against
- // the snapshot.
- match *pseudo_class {
- // For :link and :visited, we don't actually want to test the
- // element state directly.
- //
- // Instead, we use the `visited_handling` to determine if they
- // match.
- NonTSPseudoClass::Link => {
- return self.is_link() && context.visited_handling().matches_unvisited();
- },
- NonTSPseudoClass::Visited => {
- return self.is_link() && context.visited_handling().matches_visited();
- },
-
- #[cfg(feature = "gecko")]
- NonTSPseudoClass::MozTableBorderNonzero => {
- if let Some(snapshot) = self.snapshot() {
- if snapshot.has_other_pseudo_class_state() {
- return snapshot.mIsTableBorderNonzero();
- }
- }
- },
-
- #[cfg(feature = "gecko")]
- NonTSPseudoClass::MozBrowserFrame => {
- if let Some(snapshot) = self.snapshot() {
- if snapshot.has_other_pseudo_class_state() {
- return snapshot.mIsMozBrowserFrame();
- }
- }
- },
-
- #[cfg(feature = "gecko")]
- NonTSPseudoClass::MozSelectListBox => {
- if let Some(snapshot) = self.snapshot() {
- if snapshot.has_other_pseudo_class_state() {
- return snapshot.mIsSelectListBox();
- }
- }
- },
-
- // :lang() needs to match using the closest ancestor xml:lang="" or
- // lang="" attribtue from snapshots.
- NonTSPseudoClass::Lang(ref lang_arg) => {
- return self
- .element
- .match_element_lang(Some(self.get_lang()), lang_arg);
- },
-
- _ => {},
- }
-
- let flag = pseudo_class.state_flag();
- if flag.is_empty() {
- return self
- .element
- .match_non_ts_pseudo_class(pseudo_class, context);
- }
- match self.snapshot().and_then(|s| s.state()) {
- Some(snapshot_state) => snapshot_state.intersects(flag),
- None => self
- .element
- .match_non_ts_pseudo_class(pseudo_class, context),
- }
- }
-
- fn apply_selector_flags(&self, _flags: ElementSelectorFlags) {
- debug_assert!(false, "Shouldn't need selector flags for invalidation");
- }
-
- fn match_pseudo_element(
- &self,
- pseudo_element: &PseudoElement,
- context: &mut MatchingContext<Self::Impl>,
- ) -> bool {
- self.element.match_pseudo_element(pseudo_element, context)
- }
-
- fn is_link(&self) -> bool {
- match self.snapshot().and_then(|s| s.state()) {
- Some(state) => state.intersects(ElementState::VISITED_OR_UNVISITED),
- None => self.element.is_link(),
- }
- }
-
- fn opaque(&self) -> OpaqueElement {
- self.element.opaque()
- }
-
- fn parent_element(&self) -> Option<Self> {
- let parent = self.element.parent_element()?;
- Some(Self::new(parent, self.snapshot_map))
- }
-
- fn parent_node_is_shadow_root(&self) -> bool {
- self.element.parent_node_is_shadow_root()
- }
-
- fn containing_shadow_host(&self) -> Option<Self> {
- let host = self.element.containing_shadow_host()?;
- Some(Self::new(host, self.snapshot_map))
- }
-
- fn prev_sibling_element(&self) -> Option<Self> {
- let sibling = self.element.prev_sibling_element()?;
- Some(Self::new(sibling, self.snapshot_map))
- }
-
- fn next_sibling_element(&self) -> Option<Self> {
- let sibling = self.element.next_sibling_element()?;
- Some(Self::new(sibling, self.snapshot_map))
- }
-
- fn first_element_child(&self) -> Option<Self> {
- let child = self.element.first_element_child()?;
- Some(Self::new(child, self.snapshot_map))
- }
-
- #[inline]
- fn is_html_element_in_html_document(&self) -> bool {
- self.element.is_html_element_in_html_document()
- }
-
- #[inline]
- fn is_html_slot_element(&self) -> bool {
- self.element.is_html_slot_element()
- }
-
- #[inline]
- fn has_local_name(
- &self,
- local_name: &<Self::Impl as ::selectors::SelectorImpl>::BorrowedLocalName,
- ) -> bool {
- self.element.has_local_name(local_name)
- }
-
- #[inline]
- fn has_namespace(
- &self,
- ns: &<Self::Impl as ::selectors::SelectorImpl>::BorrowedNamespaceUrl,
- ) -> bool {
- self.element.has_namespace(ns)
- }
-
- #[inline]
- fn is_same_type(&self, other: &Self) -> bool {
- self.element.is_same_type(&other.element)
- }
-
- fn attr_matches(
- &self,
- ns: &NamespaceConstraint<&Namespace>,
- local_name: &LocalName,
- operation: &AttrSelectorOperation<&AttrValue>,
- ) -> bool {
- match self.snapshot() {
- Some(snapshot) if snapshot.has_attrs() => {
- snapshot.attr_matches(ns, local_name, operation)
- },
- _ => self.element.attr_matches(ns, local_name, operation),
- }
- }
-
- fn has_id(&self, id: &AtomIdent, case_sensitivity: CaseSensitivity) -> bool {
- match self.snapshot() {
- Some(snapshot) if snapshot.has_attrs() => snapshot
- .id_attr()
- .map_or(false, |atom| case_sensitivity.eq_atom(&atom, id)),
- _ => self.element.has_id(id, case_sensitivity),
- }
- }
-
- fn is_part(&self, name: &AtomIdent) -> bool {
- match self.snapshot() {
- Some(snapshot) if snapshot.has_attrs() => snapshot.is_part(name),
- _ => self.element.is_part(name),
- }
- }
-
- fn imported_part(&self, name: &AtomIdent) -> Option<AtomIdent> {
- match self.snapshot() {
- Some(snapshot) if snapshot.has_attrs() => snapshot.imported_part(name),
- _ => self.element.imported_part(name),
- }
- }
-
- fn has_class(&self, name: &AtomIdent, case_sensitivity: CaseSensitivity) -> bool {
- match self.snapshot() {
- Some(snapshot) if snapshot.has_attrs() => snapshot.has_class(name, case_sensitivity),
- _ => self.element.has_class(name, case_sensitivity),
- }
- }
-
- fn is_empty(&self) -> bool {
- self.element.is_empty()
- }
-
- fn is_root(&self) -> bool {
- self.element.is_root()
- }
-
- fn is_pseudo_element(&self) -> bool {
- self.element.is_pseudo_element()
- }
-
- fn pseudo_element_originating_element(&self) -> Option<Self> {
- self.element
- .pseudo_element_originating_element()
- .map(|e| ElementWrapper::new(e, self.snapshot_map))
- }
-
- fn assigned_slot(&self) -> Option<Self> {
- self.element
- .assigned_slot()
- .map(|e| ElementWrapper::new(e, self.snapshot_map))
- }
-}
diff --git a/components/style/invalidation/element/invalidation_map.rs b/components/style/invalidation/element/invalidation_map.rs
deleted file mode 100644
index 1cfc2fa7c05..00000000000
--- a/components/style/invalidation/element/invalidation_map.rs
+++ /dev/null
@@ -1,547 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
-
-//! Code for invalidations due to state or attribute changes.
-
-use crate::context::QuirksMode;
-use crate::selector_map::{
- MaybeCaseInsensitiveHashMap, PrecomputedHashMap, SelectorMap, SelectorMapEntry,
-};
-use crate::selector_parser::SelectorImpl;
-use crate::AllocErr;
-use crate::{Atom, LocalName, Namespace, ShrinkIfNeeded};
-use selectors::attr::NamespaceConstraint;
-use selectors::parser::{Combinator, Component};
-use selectors::parser::{Selector, SelectorIter};
-use selectors::visitor::{SelectorListKind, SelectorVisitor};
-use smallvec::SmallVec;
-use style_traits::dom::{DocumentState, ElementState};
-
-/// Mapping between (partial) CompoundSelectors (and the combinator to their
-/// right) and the states and attributes they depend on.
-///
-/// In general, for all selectors in all applicable stylesheets of the form:
-///
-/// |a _ b _ c _ d _ e|
-///
-/// Where:
-/// * |b| and |d| are simple selectors that depend on state (like :hover) or
-/// attributes (like [attr...], .foo, or #foo).
-/// * |a|, |c|, and |e| are arbitrary simple selectors that do not depend on
-/// state or attributes.
-///
-/// We generate a Dependency for both |a _ b:X _| and |a _ b:X _ c _ d:Y _|,
-/// even though those selectors may not appear on their own in any stylesheet.
-/// This allows us to quickly scan through the dependency sites of all style
-/// rules and determine the maximum effect that a given state or attribute
-/// change may have on the style of elements in the document.
-#[derive(Clone, Debug, MallocSizeOf)]
-pub struct Dependency {
- /// The dependency selector.
- #[cfg_attr(
- feature = "gecko",
- ignore_malloc_size_of = "CssRules have primary refs, we measure there"
- )]
- #[cfg_attr(feature = "servo", ignore_malloc_size_of = "Arc")]
- pub selector: Selector<SelectorImpl>,
-
- /// The offset into the selector that we should match on.
- pub selector_offset: usize,
-
- /// The parent dependency for an ancestor selector. For example, consider
- /// the following:
- ///
- /// .foo .bar:where(.baz span) .qux
- /// ^ ^ ^
- /// A B C
- ///
- /// We'd generate:
- ///
- /// * One dependency for .qux (offset: 0, parent: None)
- /// * One dependency for .baz pointing to B with parent being a
- /// dependency pointing to C.
- /// * One dependency from .bar pointing to C (parent: None)
- /// * One dependency from .foo pointing to A (parent: None)
- ///
- pub parent: Option<Box<Dependency>>,
-}
-
-size_of_test!(Dependency, 24);
-
-/// The kind of elements down the tree this dependency may affect.
-#[derive(Debug, Eq, PartialEq)]
-pub enum DependencyInvalidationKind {
- /// This dependency may affect the element that changed itself.
- Element,
- /// This dependency affects the style of the element itself, and also the
- /// style of its descendants.
- ///
- /// TODO(emilio): Each time this feels more of a hack for eager pseudos...
- ElementAndDescendants,
- /// This dependency may affect descendants down the tree.
- Descendants,
- /// This dependency may affect siblings to the right of the element that
- /// changed.
- Siblings,
- /// This dependency may affect slotted elements of the element that changed.
- SlottedElements,
- /// This dependency may affect parts of the element that changed.
- Parts,
-}
-
-impl Dependency {
- /// Creates a dummy dependency to invalidate the whole selector.
- ///
- /// This is necessary because document state invalidation wants to
- /// invalidate all elements in the document.
- ///
- /// The offset is such as that Invalidation::new(self) returns a zero
- /// offset. That is, it points to a virtual "combinator" outside of the
- /// selector, so calling combinator() on such a dependency will panic.
- pub fn for_full_selector_invalidation(selector: Selector<SelectorImpl>) -> Self {
- Self {
- selector_offset: selector.len() + 1,
- selector,
- parent: None,
- }
- }
-
- /// Returns the combinator to the right of the partial selector this
- /// dependency represents.
- ///
- /// TODO(emilio): Consider storing inline if it helps cache locality?
- pub fn combinator(&self) -> Option<Combinator> {
- if self.selector_offset == 0 {
- return None;
- }
-
- Some(
- self.selector
- .combinator_at_match_order(self.selector_offset - 1),
- )
- }
-
- /// The kind of invalidation that this would generate.
- pub fn invalidation_kind(&self) -> DependencyInvalidationKind {
- match self.combinator() {
- None => DependencyInvalidationKind::Element,
- Some(Combinator::Child) | Some(Combinator::Descendant) => {
- DependencyInvalidationKind::Descendants
- },
- Some(Combinator::LaterSibling) | Some(Combinator::NextSibling) => {
- DependencyInvalidationKind::Siblings
- },
- // TODO(emilio): We could look at the selector itself to see if it's
- // an eager pseudo, and return only Descendants here if not.
- Some(Combinator::PseudoElement) => DependencyInvalidationKind::ElementAndDescendants,
- Some(Combinator::SlotAssignment) => DependencyInvalidationKind::SlottedElements,
- Some(Combinator::Part) => DependencyInvalidationKind::Parts,
- }
- }
-}
-
-impl SelectorMapEntry for Dependency {
- fn selector(&self) -> SelectorIter<SelectorImpl> {
- self.selector.iter_from(self.selector_offset)
- }
-}
-
-/// The same, but for state selectors, which can track more exactly what state
-/// do they track.
-#[derive(Clone, Debug, MallocSizeOf)]
-pub struct StateDependency {
- /// The other dependency fields.
- pub dep: Dependency,
- /// The state this dependency is affected by.
- pub state: ElementState,
-}
-
-impl SelectorMapEntry for StateDependency {
- fn selector(&self) -> SelectorIter<SelectorImpl> {
- self.dep.selector()
- }
-}
-
-/// The same, but for document state selectors.
-#[derive(Clone, Debug, MallocSizeOf)]
-pub struct DocumentStateDependency {
- /// We track `Dependency` even though we don't need to track an offset,
- /// since when it changes it changes for the whole document anyway.
- #[cfg_attr(
- feature = "gecko",
- ignore_malloc_size_of = "CssRules have primary refs, we measure there"
- )]
- #[cfg_attr(feature = "servo", ignore_malloc_size_of = "Arc")]
- pub dependency: Dependency,
- /// The state this dependency is affected by.
- pub state: DocumentState,
-}
-
-/// A map where we store invalidations.
-///
-/// This is slightly different to a SelectorMap, in the sense of that the same
-/// selector may appear multiple times.
-///
-/// In particular, we want to lookup as few things as possible to get the fewer
-/// selectors the better, so this looks up by id, class, or looks at the list of
-/// state/other attribute affecting selectors.
-#[derive(Clone, Debug, MallocSizeOf)]
-pub struct InvalidationMap {
- /// A map from a given class name to all the selectors with that class
- /// selector.
- pub class_to_selector: MaybeCaseInsensitiveHashMap<Atom, SmallVec<[Dependency; 1]>>,
- /// A map from a given id to all the selectors with that ID in the
- /// stylesheets currently applying to the document.
- pub id_to_selector: MaybeCaseInsensitiveHashMap<Atom, SmallVec<[Dependency; 1]>>,
- /// A map of all the state dependencies.
- pub state_affecting_selectors: SelectorMap<StateDependency>,
- /// A list of document state dependencies in the rules we represent.
- pub document_state_selectors: Vec<DocumentStateDependency>,
- /// A map of other attribute affecting selectors.
- pub other_attribute_affecting_selectors:
- PrecomputedHashMap<LocalName, SmallVec<[Dependency; 1]>>,
-}
-
-impl InvalidationMap {
- /// Creates an empty `InvalidationMap`.
- pub fn new() -> Self {
- Self {
- class_to_selector: MaybeCaseInsensitiveHashMap::new(),
- id_to_selector: MaybeCaseInsensitiveHashMap::new(),
- state_affecting_selectors: SelectorMap::new(),
- document_state_selectors: Vec::new(),
- other_attribute_affecting_selectors: PrecomputedHashMap::default(),
- }
- }
-
- /// Returns the number of dependencies stored in the invalidation map.
- pub fn len(&self) -> usize {
- self.state_affecting_selectors.len() +
- self.document_state_selectors.len() +
- self.other_attribute_affecting_selectors
- .iter()
- .fold(0, |accum, (_, ref v)| accum + v.len()) +
- self.id_to_selector
- .iter()
- .fold(0, |accum, (_, ref v)| accum + v.len()) +
- self.class_to_selector
- .iter()
- .fold(0, |accum, (_, ref v)| accum + v.len())
- }
-
- /// Clears this map, leaving it empty.
- pub fn clear(&mut self) {
- self.class_to_selector.clear();
- self.id_to_selector.clear();
- self.state_affecting_selectors.clear();
- self.document_state_selectors.clear();
- self.other_attribute_affecting_selectors.clear();
- }
-
- /// Shrink the capacity of hash maps if needed.
- pub fn shrink_if_needed(&mut self) {
- self.class_to_selector.shrink_if_needed();
- self.id_to_selector.shrink_if_needed();
- self.state_affecting_selectors.shrink_if_needed();
- self.other_attribute_affecting_selectors.shrink_if_needed();
- }
-
- /// Adds a selector to this `InvalidationMap`. Returns Err(..) to
- /// signify OOM.
- pub fn note_selector(
- &mut self,
- selector: &Selector<SelectorImpl>,
- quirks_mode: QuirksMode,
- ) -> Result<(), AllocErr> {
- debug!("InvalidationMap::note_selector({:?})", selector);
-
- let mut document_state = DocumentState::empty();
-
- {
- let mut parent_stack = SmallVec::new();
- let mut alloc_error = None;
- let mut collector = SelectorDependencyCollector {
- map: self,
- document_state: &mut document_state,
- selector,
- parent_selectors: &mut parent_stack,
- quirks_mode,
- compound_state: PerCompoundState::new(0),
- alloc_error: &mut alloc_error,
- };
-
- let visit_result = collector.visit_whole_selector();
- debug_assert_eq!(!visit_result, alloc_error.is_some());
- if let Some(alloc_error) = alloc_error {
- return Err(alloc_error);
- }
- }
-
- if !document_state.is_empty() {
- let dep = DocumentStateDependency {
- state: document_state,
- dependency: Dependency::for_full_selector_invalidation(selector.clone()),
- };
- self.document_state_selectors.try_reserve(1)?;
- self.document_state_selectors.push(dep);
- }
-
- Ok(())
- }
-}
-
-struct PerCompoundState {
- /// The offset at which our compound starts.
- offset: usize,
-
- /// The state this compound selector is affected by.
- element_state: ElementState,
-}
-
-impl PerCompoundState {
- fn new(offset: usize) -> Self {
- Self {
- offset,
- element_state: ElementState::empty(),
- }
- }
-}
-
-/// A struct that collects invalidations for a given compound selector.
-struct SelectorDependencyCollector<'a> {
- map: &'a mut InvalidationMap,
-
- /// The document this _complex_ selector is affected by.
- ///
- /// We don't need to track state per compound selector, since it's global
- /// state and it changes for everything.
- document_state: &'a mut DocumentState,
-
- /// The current selector and offset we're iterating.
- selector: &'a Selector<SelectorImpl>,
-
- /// The stack of parent selectors that we have, and at which offset of the
- /// sequence.
- ///
- /// This starts empty. It grows when we find nested :is and :where selector
- /// lists.
- parent_selectors: &'a mut SmallVec<[(Selector<SelectorImpl>, usize); 5]>,
-
- /// The quirks mode of the document where we're inserting dependencies.
- quirks_mode: QuirksMode,
-
- /// State relevant to a given compound selector.
- compound_state: PerCompoundState,
-
- /// The allocation error, if we OOM.
- alloc_error: &'a mut Option<AllocErr>,
-}
-
-impl<'a> SelectorDependencyCollector<'a> {
- fn visit_whole_selector(&mut self) -> bool {
- let iter = self.selector.iter();
- self.visit_whole_selector_from(iter, 0)
- }
-
- fn visit_whole_selector_from(
- &mut self,
- mut iter: SelectorIter<SelectorImpl>,
- mut index: usize,
- ) -> bool {
- loop {
- // Reset the compound state.
- self.compound_state = PerCompoundState::new(index);
-
- // Visit all the simple selectors in this sequence.
- for ss in &mut iter {
- if !ss.visit(self) {
- return false;
- }
- index += 1; // Account for the simple selector.
- }
-
- if !self.compound_state.element_state.is_empty() {
- let dependency = self.dependency();
- let result = self.map.state_affecting_selectors.insert(
- StateDependency {
- dep: dependency,
- state: self.compound_state.element_state,
- },
- self.quirks_mode,
- );
- if let Err(alloc_error) = result {
- *self.alloc_error = Some(alloc_error.into());
- return false;
- }
- }
-
- let combinator = iter.next_sequence();
- if combinator.is_none() {
- return true;
- }
- index += 1; // account for the combinator
- }
- }
-
- fn add_attr_dependency(&mut self, name: LocalName) -> bool {
- let dependency = self.dependency();
-
- let map = &mut self.map.other_attribute_affecting_selectors;
- if let Err(err) = map.try_reserve(1) {
- *self.alloc_error = Some(err.into());
- return false;
- }
- let vec = map.entry(name).or_default();
- if let Err(err) = vec.try_reserve(1) {
- *self.alloc_error = Some(err.into());
- return false;
- }
- vec.push(dependency);
- true
- }
-
- fn dependency(&self) -> Dependency {
- let mut parent = None;
-
- // TODO(emilio): Maybe we should refcount the parent dependencies, or
- // cache them or something.
- for &(ref selector, ref selector_offset) in self.parent_selectors.iter() {
- debug_assert_ne!(
- self.compound_state.offset, 0,
- "Shouldn't bother creating nested dependencies for the rightmost compound",
- );
- let new_parent = Dependency {
- selector: selector.clone(),
- selector_offset: *selector_offset,
- parent,
- };
- parent = Some(Box::new(new_parent));
- }
-
- Dependency {
- selector: self.selector.clone(),
- selector_offset: self.compound_state.offset,
- parent,
- }
- }
-}
-
-impl<'a> SelectorVisitor for SelectorDependencyCollector<'a> {
- type Impl = SelectorImpl;
-
- fn visit_selector_list(
- &mut self,
- _list_kind: SelectorListKind,
- list: &[Selector<SelectorImpl>],
- ) -> bool {
- for selector in list {
- // Here we cheat a bit: We can visit the rightmost compound with
- // the "outer" visitor, and it'd be fine. This reduces the amount of
- // state and attribute invalidations, and we need to check the outer
- // selector to the left anyway to avoid over-invalidation, so it
- // avoids matching it twice uselessly.
- let mut iter = selector.iter();
- let mut index = 0;
-
- for ss in &mut iter {
- if !ss.visit(self) {
- return false;
- }
- index += 1;
- }
-
- let combinator = iter.next_sequence();
- if combinator.is_none() {
- continue;
- }
-
- index += 1; // account for the combinator.
-
- self.parent_selectors
- .push((self.selector.clone(), self.compound_state.offset));
- let mut nested = SelectorDependencyCollector {
- map: &mut *self.map,
- document_state: &mut *self.document_state,
- selector,
- parent_selectors: &mut *self.parent_selectors,
- quirks_mode: self.quirks_mode,
- compound_state: PerCompoundState::new(index),
- alloc_error: &mut *self.alloc_error,
- };
- if !nested.visit_whole_selector_from(iter, index) {
- return false;
- }
- self.parent_selectors.pop();
- }
- true
- }
-
- fn visit_simple_selector(&mut self, s: &Component<SelectorImpl>) -> bool {
- use crate::selector_parser::NonTSPseudoClass;
-
- match *s {
- Component::ID(ref atom) | Component::Class(ref atom) => {
- let dependency = self.dependency();
- let map = match *s {
- Component::ID(..) => &mut self.map.id_to_selector,
- Component::Class(..) => &mut self.map.class_to_selector,
- _ => unreachable!(),
- };
- let entry = match map.try_entry(atom.0.clone(), self.quirks_mode) {
- Ok(entry) => entry,
- Err(err) => {
- *self.alloc_error = Some(err.into());
- return false;
- },
- };
- let vec = entry.or_insert_with(SmallVec::new);
- if let Err(err) = vec.try_reserve(1) {
- *self.alloc_error = Some(err.into());
- return false;
- }
- vec.push(dependency);
- true
- },
- Component::NonTSPseudoClass(ref pc) => {
- self.compound_state.element_state |= pc.state_flag();
- *self.document_state |= pc.document_state_flag();
-
- let attr_name = match *pc {
- #[cfg(feature = "gecko")]
- NonTSPseudoClass::MozTableBorderNonzero => local_name!("border"),
- #[cfg(feature = "gecko")]
- NonTSPseudoClass::MozBrowserFrame => local_name!("mozbrowser"),
- #[cfg(feature = "gecko")]
- NonTSPseudoClass::MozSelectListBox => {
- // This depends on two attributes.
- return self.add_attr_dependency(local_name!("multiple")) &&
- self.add_attr_dependency(local_name!("size"));
- },
- NonTSPseudoClass::Lang(..) => local_name!("lang"),
- _ => return true,
- };
-
- self.add_attr_dependency(attr_name)
- },
- _ => true,
- }
- }
-
- fn visit_attribute_selector(
- &mut self,
- _: &NamespaceConstraint<&Namespace>,
- local_name: &LocalName,
- local_name_lower: &LocalName,
- ) -> bool {
- if !self.add_attr_dependency(local_name.clone()) {
- return false;
- }
-
- if local_name != local_name_lower && !self.add_attr_dependency(local_name_lower.clone()) {
- return false;
- }
-
- true
- }
-}
diff --git a/components/style/invalidation/element/invalidator.rs b/components/style/invalidation/element/invalidator.rs
deleted file mode 100644
index 00f714c5b15..00000000000
--- a/components/style/invalidation/element/invalidator.rs
+++ /dev/null
@@ -1,1017 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
-
-//! The struct that takes care of encapsulating all the logic on where and how
-//! element styles need to be invalidated.
-
-use crate::context::StackLimitChecker;
-use crate::dom::{TElement, TNode, TShadowRoot};
-use crate::invalidation::element::invalidation_map::{Dependency, DependencyInvalidationKind};
-use selectors::matching::matches_compound_selector_from;
-use selectors::matching::{CompoundSelectorMatchingResult, MatchingContext};
-use selectors::parser::{Combinator, Component};
-use selectors::OpaqueElement;
-use smallvec::SmallVec;
-use std::fmt;
-use std::fmt::Write;
-
-/// A trait to abstract the collection of invalidations for a given pass.
-pub trait InvalidationProcessor<'a, E>
-where
- E: TElement,
-{
- /// Whether an invalidation that contains only a pseudo-element selector
- /// like ::before or ::after triggers invalidation of the element that would
- /// originate it.
- fn invalidates_on_pseudo_element(&self) -> bool {
- false
- }
-
- /// Whether the invalidation processor only cares about light-tree
- /// descendants of a given element, that is, doesn't invalidate
- /// pseudo-elements, NAC, shadow dom...
- fn light_tree_only(&self) -> bool {
- false
- }
-
- /// When a dependency from a :where or :is selector matches, it may still be
- /// the case that we don't need to invalidate the full style. Consider the
- /// case of:
- ///
- /// div .foo:where(.bar *, .baz) .qux
- ///
- /// We can get to the `*` part after a .bar class change, but you only need
- /// to restyle the element if it also matches .foo.
- ///
- /// Similarly, you only need to restyle .baz if the whole result of matching
- /// the selector changes.
- ///
- /// This function is called to check the result of matching the "outer"
- /// dependency that we generate for the parent of the `:where` selector,
- /// that is, in the case above it should match
- /// `div .foo:where(.bar *, .baz)`.
- ///
- /// Returning true unconditionally here is over-optimistic and may
- /// over-invalidate.
- fn check_outer_dependency(&mut self, dependency: &Dependency, element: E) -> bool;
-
- /// The matching context that should be used to process invalidations.
- fn matching_context(&mut self) -> &mut MatchingContext<'a, E::Impl>;
-
- /// Collect invalidations for a given element's descendants and siblings.
- ///
- /// Returns whether the element itself was invalidated.
- fn collect_invalidations(
- &mut self,
- element: E,
- self_invalidations: &mut InvalidationVector<'a>,
- descendant_invalidations: &mut DescendantInvalidationLists<'a>,
- sibling_invalidations: &mut InvalidationVector<'a>,
- ) -> bool;
-
- /// Returns whether the invalidation process should process the descendants
- /// of the given element.
- fn should_process_descendants(&mut self, element: E) -> bool;
-
- /// Executes an arbitrary action when the recursion limit is exceded (if
- /// any).
- fn recursion_limit_exceeded(&mut self, element: E);
-
- /// Executes an action when `Self` is invalidated.
- fn invalidated_self(&mut self, element: E);
-
- /// Executes an action when `sibling` is invalidated as a sibling of
- /// `of`.
- fn invalidated_sibling(&mut self, sibling: E, of: E);
-
- /// Executes an action when any descendant of `Self` is invalidated.
- fn invalidated_descendants(&mut self, element: E, child: E);
-}
-
-/// Different invalidation lists for descendants.
-#[derive(Debug, Default)]
-pub struct DescendantInvalidationLists<'a> {
- /// Invalidations for normal DOM children and pseudo-elements.
- ///
- /// TODO(emilio): Having a list of invalidations just for pseudo-elements
- /// may save some work here and there.
- pub dom_descendants: InvalidationVector<'a>,
- /// Invalidations for slotted children of an element.
- pub slotted_descendants: InvalidationVector<'a>,
- /// Invalidations for ::part()s of an element.
- pub parts: InvalidationVector<'a>,
-}
-
-impl<'a> DescendantInvalidationLists<'a> {
- fn is_empty(&self) -> bool {
- self.dom_descendants.is_empty() &&
- self.slotted_descendants.is_empty() &&
- self.parts.is_empty()
- }
-}
-
-/// The struct that takes care of encapsulating all the logic on where and how
-/// element styles need to be invalidated.
-pub struct TreeStyleInvalidator<'a, 'b, E, P: 'a>
-where
- 'b: 'a,
- E: TElement,
- P: InvalidationProcessor<'b, E>,
-{
- element: E,
- stack_limit_checker: Option<&'a StackLimitChecker>,
- processor: &'a mut P,
- _marker: ::std::marker::PhantomData<&'b ()>,
-}
-
-/// A vector of invalidations, optimized for small invalidation sets.
-pub type InvalidationVector<'a> = SmallVec<[Invalidation<'a>; 10]>;
-
-/// The kind of descendant invalidation we're processing.
-#[derive(Clone, Copy, Debug, Eq, PartialEq)]
-enum DescendantInvalidationKind {
- /// A DOM descendant invalidation.
- Dom,
- /// A ::slotted() descendant invalidation.
- Slotted,
- /// A ::part() descendant invalidation.
- Part,
-}
-
-/// The kind of invalidation we're processing.
-///
-/// We can use this to avoid pushing invalidations of the same kind to our
-/// descendants or siblings.
-#[derive(Clone, Copy, Debug, Eq, PartialEq)]
-enum InvalidationKind {
- Descendant(DescendantInvalidationKind),
- Sibling,
-}
-
-/// An `Invalidation` is a complex selector that describes which elements,
-/// relative to a current element we are processing, must be restyled.
-#[derive(Clone)]
-pub struct Invalidation<'a> {
- /// The dependency that generated this invalidation.
- ///
- /// Note that the offset inside the dependency is not really useful after
- /// construction.
- dependency: &'a Dependency,
- /// The right shadow host from where the rule came from, if any.
- ///
- /// This is needed to ensure that we match the selector with the right
- /// state, as whether some selectors like :host and ::part() match depends
- /// on it.
- scope: Option<OpaqueElement>,
- /// The offset of the selector pointing to a compound selector.
- ///
- /// This order is a "parse order" offset, that is, zero is the leftmost part
- /// of the selector written as parsed / serialized.
- ///
- /// It is initialized from the offset from `dependency`.
- offset: usize,
- /// Whether the invalidation was already matched by any previous sibling or
- /// ancestor.
- ///
- /// If this is the case, we can avoid pushing invalidations generated by
- /// this one if the generated invalidation is effective for all the siblings
- /// or descendants after us.
- matched_by_any_previous: bool,
-}
-
-impl<'a> Invalidation<'a> {
- /// Create a new invalidation for matching a dependency.
- pub fn new(dependency: &'a Dependency, scope: Option<OpaqueElement>) -> Self {
- debug_assert!(
- dependency.selector_offset == dependency.selector.len() + 1 ||
- dependency.invalidation_kind() != DependencyInvalidationKind::Element,
- "No point to this, if the dependency matched the element we should just invalidate it"
- );
- Self {
- dependency,
- scope,
- // + 1 to go past the combinator.
- offset: dependency.selector.len() + 1 - dependency.selector_offset,
- matched_by_any_previous: false,
- }
- }
-
- /// Whether this invalidation is effective for the next sibling or
- /// descendant after us.
- fn effective_for_next(&self) -> bool {
- if self.offset == 0 {
- return true;
- }
-
- // TODO(emilio): For pseudo-elements this should be mostly false, except
- // for the weird pseudos in <input type="number">.
- //
- // We should be able to do better here!
- match self
- .dependency
- .selector
- .combinator_at_parse_order(self.offset - 1)
- {
- Combinator::Descendant | Combinator::LaterSibling | Combinator::PseudoElement => true,
- Combinator::Part |
- Combinator::SlotAssignment |
- Combinator::NextSibling |
- Combinator::Child => false,
- }
- }
-
- fn kind(&self) -> InvalidationKind {
- if self.offset == 0 {
- return InvalidationKind::Descendant(DescendantInvalidationKind::Dom);
- }
-
- match self
- .dependency
- .selector
- .combinator_at_parse_order(self.offset - 1)
- {
- Combinator::Child | Combinator::Descendant | Combinator::PseudoElement => {
- InvalidationKind::Descendant(DescendantInvalidationKind::Dom)
- },
- Combinator::Part => InvalidationKind::Descendant(DescendantInvalidationKind::Part),
- Combinator::SlotAssignment => {
- InvalidationKind::Descendant(DescendantInvalidationKind::Slotted)
- },
- Combinator::NextSibling | Combinator::LaterSibling => InvalidationKind::Sibling,
- }
- }
-}
-
-impl<'a> fmt::Debug for Invalidation<'a> {
- fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
- use cssparser::ToCss;
-
- f.write_str("Invalidation(")?;
- for component in self
- .dependency
- .selector
- .iter_raw_parse_order_from(self.offset)
- {
- if matches!(*component, Component::Combinator(..)) {
- break;
- }
- component.to_css(f)?;
- }
- f.write_char(')')
- }
-}
-
-/// The result of processing a single invalidation for a given element.
-struct SingleInvalidationResult {
- /// Whether the element itself was invalidated.
- invalidated_self: bool,
- /// Whether the invalidation matched, either invalidating the element or
- /// generating another invalidation.
- matched: bool,
-}
-
-/// The result of a whole invalidation process for a given element.
-pub struct InvalidationResult {
- /// Whether the element itself was invalidated.
- invalidated_self: bool,
- /// Whether the element's descendants were invalidated.
- invalidated_descendants: bool,
- /// Whether the element's siblings were invalidated.
- invalidated_siblings: bool,
-}
-
-impl InvalidationResult {
- /// Create an emtpy result.
- pub fn empty() -> Self {
- Self {
- invalidated_self: false,
- invalidated_descendants: false,
- invalidated_siblings: false,
- }
- }
-
- /// Whether the invalidation has invalidate the element itself.
- pub fn has_invalidated_self(&self) -> bool {
- self.invalidated_self
- }
-
- /// Whether the invalidation has invalidate desendants.
- pub fn has_invalidated_descendants(&self) -> bool {
- self.invalidated_descendants
- }
-
- /// Whether the invalidation has invalidate siblings.
- pub fn has_invalidated_siblings(&self) -> bool {
- self.invalidated_siblings
- }
-}
-
-impl<'a, 'b, E, P: 'a> TreeStyleInvalidator<'a, 'b, E, P>
-where
- 'b: 'a,
- E: TElement,
- P: InvalidationProcessor<'b, E>,
-{
- /// Trivially constructs a new `TreeStyleInvalidator`.
- pub fn new(
- element: E,
- stack_limit_checker: Option<&'a StackLimitChecker>,
- processor: &'a mut P,
- ) -> Self {
- Self {
- element,
- stack_limit_checker,
- processor,
- _marker: ::std::marker::PhantomData,
- }
- }
-
- /// Perform the invalidation pass.
- pub fn invalidate(mut self) -> InvalidationResult {
- debug!("StyleTreeInvalidator::invalidate({:?})", self.element);
-
- let mut self_invalidations = InvalidationVector::new();
- let mut descendant_invalidations = DescendantInvalidationLists::default();
- let mut sibling_invalidations = InvalidationVector::new();
-
- let mut invalidated_self = self.processor.collect_invalidations(
- self.element,
- &mut self_invalidations,
- &mut descendant_invalidations,
- &mut sibling_invalidations,
- );
-
- debug!("Collected invalidations (self: {}): ", invalidated_self);
- debug!(
- " > self: {}, {:?}",
- self_invalidations.len(),
- self_invalidations
- );
- debug!(" > descendants: {:?}", descendant_invalidations);
- debug!(
- " > siblings: {}, {:?}",
- sibling_invalidations.len(),
- sibling_invalidations
- );
-
- let invalidated_self_from_collection = invalidated_self;
-
- invalidated_self |= self.process_descendant_invalidations(
- &self_invalidations,
- &mut descendant_invalidations,
- &mut sibling_invalidations,
- DescendantInvalidationKind::Dom,
- );
-
- if invalidated_self && !invalidated_self_from_collection {
- self.processor.invalidated_self(self.element);
- }
-
- let invalidated_descendants = self.invalidate_descendants(&descendant_invalidations);
- let invalidated_siblings = self.invalidate_siblings(&mut sibling_invalidations);
-
- InvalidationResult {
- invalidated_self,
- invalidated_descendants,
- invalidated_siblings,
- }
- }
-
- /// Go through later DOM siblings, invalidating style as needed using the
- /// `sibling_invalidations` list.
- ///
- /// Returns whether any sibling's style or any sibling descendant's style
- /// was invalidated.
- fn invalidate_siblings(&mut self, sibling_invalidations: &mut InvalidationVector<'b>) -> bool {
- if sibling_invalidations.is_empty() {
- return false;
- }
-
- let mut current = self.element.next_sibling_element();
- let mut any_invalidated = false;
-
- while let Some(sibling) = current {
- let mut sibling_invalidator =
- TreeStyleInvalidator::new(sibling, self.stack_limit_checker, self.processor);
-
- let mut invalidations_for_descendants = DescendantInvalidationLists::default();
- let invalidated_sibling = sibling_invalidator.process_sibling_invalidations(
- &mut invalidations_for_descendants,
- sibling_invalidations,
- );
-
- if invalidated_sibling {
- sibling_invalidator
- .processor
- .invalidated_sibling(sibling, self.element);
- }
-
- any_invalidated |= invalidated_sibling;
-
- any_invalidated |=
- sibling_invalidator.invalidate_descendants(&invalidations_for_descendants);
-
- if sibling_invalidations.is_empty() {
- break;
- }
-
- current = sibling.next_sibling_element();
- }
-
- any_invalidated
- }
-
- fn invalidate_pseudo_element_or_nac(
- &mut self,
- child: E,
- invalidations: &[Invalidation<'b>],
- ) -> bool {
- let mut sibling_invalidations = InvalidationVector::new();
-
- let result = self.invalidate_child(
- child,
- invalidations,
- &mut sibling_invalidations,
- DescendantInvalidationKind::Dom,
- );
-
- // Roots of NAC subtrees can indeed generate sibling invalidations, but
- // they can be just ignored, since they have no siblings.
- //
- // Note that we can end up testing selectors that wouldn't end up
- // matching due to this being NAC, like those coming from document
- // rules, but we overinvalidate instead of checking this.
-
- result
- }
-
- /// Invalidate a child and recurse down invalidating its descendants if
- /// needed.
- fn invalidate_child(
- &mut self,
- child: E,
- invalidations: &[Invalidation<'b>],
- sibling_invalidations: &mut InvalidationVector<'b>,
- descendant_invalidation_kind: DescendantInvalidationKind,
- ) -> bool {
- let mut invalidations_for_descendants = DescendantInvalidationLists::default();
-
- let mut invalidated_child = false;
- let invalidated_descendants = {
- let mut child_invalidator =
- TreeStyleInvalidator::new(child, self.stack_limit_checker, self.processor);
-
- invalidated_child |= child_invalidator.process_sibling_invalidations(
- &mut invalidations_for_descendants,
- sibling_invalidations,
- );
-
- invalidated_child |= child_invalidator.process_descendant_invalidations(
- invalidations,
- &mut invalidations_for_descendants,
- sibling_invalidations,
- descendant_invalidation_kind,
- );
-
- if invalidated_child {
- child_invalidator.processor.invalidated_self(child);
- }
-
- child_invalidator.invalidate_descendants(&invalidations_for_descendants)
- };
-
- // The child may not be a flattened tree child of the current element,
- // but may be arbitrarily deep.
- //
- // Since we keep the traversal flags in terms of the flattened tree,
- // we need to propagate it as appropriate.
- if invalidated_child || invalidated_descendants {
- self.processor.invalidated_descendants(self.element, child);
- }
-
- invalidated_child || invalidated_descendants
- }
-
- fn invalidate_nac(&mut self, invalidations: &[Invalidation<'b>]) -> bool {
- let mut any_nac_root = false;
-
- let element = self.element;
- element.each_anonymous_content_child(|nac| {
- any_nac_root |= self.invalidate_pseudo_element_or_nac(nac, invalidations);
- });
-
- any_nac_root
- }
-
- // NB: It's important that this operates on DOM children, which is what
- // selector-matching operates on.
- fn invalidate_dom_descendants_of(
- &mut self,
- parent: E::ConcreteNode,
- invalidations: &[Invalidation<'b>],
- ) -> bool {
- let mut any_descendant = false;
-
- let mut sibling_invalidations = InvalidationVector::new();
- for child in parent.dom_children() {
- let child = match child.as_element() {
- Some(e) => e,
- None => continue,
- };
-
- any_descendant |= self.invalidate_child(
- child,
- invalidations,
- &mut sibling_invalidations,
- DescendantInvalidationKind::Dom,
- );
- }
-
- any_descendant
- }
-
- fn invalidate_parts_in_shadow_tree(
- &mut self,
- shadow: <E::ConcreteNode as TNode>::ConcreteShadowRoot,
- invalidations: &[Invalidation<'b>],
- ) -> bool {
- debug_assert!(!invalidations.is_empty());
-
- let mut any = false;
- let mut sibling_invalidations = InvalidationVector::new();
-
- for node in shadow.as_node().dom_descendants() {
- let element = match node.as_element() {
- Some(e) => e,
- None => continue,
- };
-
- if element.has_part_attr() {
- any |= self.invalidate_child(
- element,
- invalidations,
- &mut sibling_invalidations,
- DescendantInvalidationKind::Part,
- );
- debug_assert!(
- sibling_invalidations.is_empty(),
- "::part() shouldn't have sibling combinators to the right, \
- this makes no sense! {:?}",
- sibling_invalidations
- );
- }
-
- if let Some(shadow) = element.shadow_root() {
- if element.exports_any_part() {
- any |= self.invalidate_parts_in_shadow_tree(shadow, invalidations)
- }
- }
- }
-
- any
- }
-
- fn invalidate_parts(&mut self, invalidations: &[Invalidation<'b>]) -> bool {
- if invalidations.is_empty() {
- return false;
- }
-
- let shadow = match self.element.shadow_root() {
- Some(s) => s,
- None => return false,
- };
-
- self.invalidate_parts_in_shadow_tree(shadow, invalidations)
- }
-
- fn invalidate_slotted_elements(&mut self, invalidations: &[Invalidation<'b>]) -> bool {
- if invalidations.is_empty() {
- return false;
- }
-
- let slot = self.element;
- self.invalidate_slotted_elements_in_slot(slot, invalidations)
- }
-
- fn invalidate_slotted_elements_in_slot(
- &mut self,
- slot: E,
- invalidations: &[Invalidation<'b>],
- ) -> bool {
- let mut any = false;
-
- let mut sibling_invalidations = InvalidationVector::new();
- for node in slot.slotted_nodes() {
- let element = match node.as_element() {
- Some(e) => e,
- None => continue,
- };
-
- if element.is_html_slot_element() {
- any |= self.invalidate_slotted_elements_in_slot(element, invalidations);
- } else {
- any |= self.invalidate_child(
- element,
- invalidations,
- &mut sibling_invalidations,
- DescendantInvalidationKind::Slotted,
- );
- }
-
- debug_assert!(
- sibling_invalidations.is_empty(),
- "::slotted() shouldn't have sibling combinators to the right, \
- this makes no sense! {:?}",
- sibling_invalidations
- );
- }
-
- any
- }
-
- fn invalidate_non_slotted_descendants(&mut self, invalidations: &[Invalidation<'b>]) -> bool {
- if invalidations.is_empty() {
- return false;
- }
-
- if self.processor.light_tree_only() {
- let node = self.element.as_node();
- return self.invalidate_dom_descendants_of(node, invalidations);
- }
-
- let mut any_descendant = false;
-
- // NOTE(emilio): This is only needed for Shadow DOM to invalidate
- // correctly on :host(..) changes. Instead of doing this, we could add
- // a third kind of invalidation list that walks shadow root children,
- // but it's not clear it's worth it.
- //
- // Also, it's needed as of right now for document state invalidation,
- // where we rely on iterating every element that ends up in the composed
- // doc, but we could fix that invalidating per subtree.
- if let Some(root) = self.element.shadow_root() {
- any_descendant |= self.invalidate_dom_descendants_of(root.as_node(), invalidations);
- }
-
- if let Some(marker) = self.element.marker_pseudo_element() {
- any_descendant |= self.invalidate_pseudo_element_or_nac(marker, invalidations);
- }
-
- if let Some(before) = self.element.before_pseudo_element() {
- any_descendant |= self.invalidate_pseudo_element_or_nac(before, invalidations);
- }
-
- let node = self.element.as_node();
- any_descendant |= self.invalidate_dom_descendants_of(node, invalidations);
-
- if let Some(after) = self.element.after_pseudo_element() {
- any_descendant |= self.invalidate_pseudo_element_or_nac(after, invalidations);
- }
-
- any_descendant |= self.invalidate_nac(invalidations);
-
- any_descendant
- }
-
- /// Given the descendant invalidation lists, go through the current
- /// element's descendants, and invalidate style on them.
- fn invalidate_descendants(&mut self, invalidations: &DescendantInvalidationLists<'b>) -> bool {
- if invalidations.is_empty() {
- return false;
- }
-
- debug!(
- "StyleTreeInvalidator::invalidate_descendants({:?})",
- self.element
- );
- debug!(" > {:?}", invalidations);
-
- let should_process = self.processor.should_process_descendants(self.element);
-
- if !should_process {
- return false;
- }
-
- if let Some(checker) = self.stack_limit_checker {
- if checker.limit_exceeded() {
- self.processor.recursion_limit_exceeded(self.element);
- return true;
- }
- }
-
- let mut any_descendant = false;
-
- any_descendant |= self.invalidate_non_slotted_descendants(&invalidations.dom_descendants);
- any_descendant |= self.invalidate_slotted_elements(&invalidations.slotted_descendants);
- any_descendant |= self.invalidate_parts(&invalidations.parts);
-
- any_descendant
- }
-
- /// Process the given sibling invalidations coming from our previous
- /// sibling.
- ///
- /// The sibling invalidations are somewhat special because they can be
- /// modified on the fly. New invalidations may be added and removed.
- ///
- /// In particular, all descendants get the same set of invalidations from
- /// the parent, but the invalidations from a given sibling depend on the
- /// ones we got from the previous one.
- ///
- /// Returns whether invalidated the current element's style.
- fn process_sibling_invalidations(
- &mut self,
- descendant_invalidations: &mut DescendantInvalidationLists<'b>,
- sibling_invalidations: &mut InvalidationVector<'b>,
- ) -> bool {
- let mut i = 0;
- let mut new_sibling_invalidations = InvalidationVector::new();
- let mut invalidated_self = false;
-
- while i < sibling_invalidations.len() {
- let result = self.process_invalidation(
- &sibling_invalidations[i],
- descendant_invalidations,
- &mut new_sibling_invalidations,
- InvalidationKind::Sibling,
- );
-
- invalidated_self |= result.invalidated_self;
- sibling_invalidations[i].matched_by_any_previous |= result.matched;
- if sibling_invalidations[i].effective_for_next() {
- i += 1;
- } else {
- sibling_invalidations.remove(i);
- }
- }
-
- sibling_invalidations.extend(new_sibling_invalidations.drain(..));
- invalidated_self
- }
-
- /// Process a given invalidation list coming from our parent,
- /// adding to `descendant_invalidations` and `sibling_invalidations` as
- /// needed.
- ///
- /// Returns whether our style was invalidated as a result.
- fn process_descendant_invalidations(
- &mut self,
- invalidations: &[Invalidation<'b>],
- descendant_invalidations: &mut DescendantInvalidationLists<'b>,
- sibling_invalidations: &mut InvalidationVector<'b>,
- descendant_invalidation_kind: DescendantInvalidationKind,
- ) -> bool {
- let mut invalidated = false;
-
- for invalidation in invalidations {
- let result = self.process_invalidation(
- invalidation,
- descendant_invalidations,
- sibling_invalidations,
- InvalidationKind::Descendant(descendant_invalidation_kind),
- );
-
- invalidated |= result.invalidated_self;
- if invalidation.effective_for_next() {
- let mut invalidation = invalidation.clone();
- invalidation.matched_by_any_previous |= result.matched;
- debug_assert_eq!(
- descendant_invalidation_kind,
- DescendantInvalidationKind::Dom,
- "Slotted or part invalidations don't propagate."
- );
- descendant_invalidations.dom_descendants.push(invalidation);
- }
- }
-
- invalidated
- }
-
- /// Processes a given invalidation, potentially invalidating the style of
- /// the current element.
- ///
- /// Returns whether invalidated the style of the element, and whether the
- /// invalidation should be effective to subsequent siblings or descendants
- /// down in the tree.
- fn process_invalidation(
- &mut self,
- invalidation: &Invalidation<'b>,
- descendant_invalidations: &mut DescendantInvalidationLists<'b>,
- sibling_invalidations: &mut InvalidationVector<'b>,
- invalidation_kind: InvalidationKind,
- ) -> SingleInvalidationResult {
- debug!(
- "TreeStyleInvalidator::process_invalidation({:?}, {:?}, {:?})",
- self.element, invalidation, invalidation_kind
- );
-
- let matching_result = {
- let context = self.processor.matching_context();
- context.current_host = invalidation.scope;
-
- matches_compound_selector_from(
- &invalidation.dependency.selector,
- invalidation.offset,
- context,
- &self.element,
- )
- };
-
- let next_invalidation = match matching_result {
- CompoundSelectorMatchingResult::NotMatched => {
- return SingleInvalidationResult {
- invalidated_self: false,
- matched: false,
- }
- },
- CompoundSelectorMatchingResult::FullyMatched => {
- debug!(" > Invalidation matched completely");
- // We matched completely. If we're an inner selector now we need
- // to go outside our selector and carry on invalidating.
- let mut cur_dependency = invalidation.dependency;
- loop {
- cur_dependency = match cur_dependency.parent {
- None => {
- return SingleInvalidationResult {
- invalidated_self: true,
- matched: true,
- }
- },
- Some(ref p) => &**p,
- };
-
- debug!(" > Checking outer dependency {:?}", cur_dependency);
-
- // The inner selector changed, now check if the full
- // previous part of the selector did, before keeping
- // checking for descendants.
- if !self
- .processor
- .check_outer_dependency(cur_dependency, self.element)
- {
- return SingleInvalidationResult {
- invalidated_self: false,
- matched: false,
- };
- }
-
- if cur_dependency.invalidation_kind() == DependencyInvalidationKind::Element {
- continue;
- }
-
- debug!(" > Generating invalidation");
- break Invalidation::new(cur_dependency, invalidation.scope);
- }
- },
- CompoundSelectorMatchingResult::Matched {
- next_combinator_offset,
- } => Invalidation {
- dependency: invalidation.dependency,
- scope: invalidation.scope,
- offset: next_combinator_offset + 1,
- matched_by_any_previous: false,
- },
- };
-
- debug_assert_ne!(
- next_invalidation.offset, 0,
- "Rightmost selectors shouldn't generate more invalidations",
- );
-
- let mut invalidated_self = false;
- let next_combinator = next_invalidation
- .dependency
- .selector
- .combinator_at_parse_order(next_invalidation.offset - 1);
-
- if matches!(next_combinator, Combinator::PseudoElement) &&
- self.processor.invalidates_on_pseudo_element()
- {
- // We need to invalidate the element whenever pseudos change, for
- // two reasons:
- //
- // * Eager pseudo styles are stored as part of the originating
- // element's computed style.
- //
- // * Lazy pseudo-styles might be cached on the originating
- // element's pseudo-style cache.
- //
- // This could be more fine-grained (perhaps with a RESTYLE_PSEUDOS
- // hint?).
- //
- // Note that we'll also restyle the pseudo-element because it would
- // match this invalidation.
- //
- // FIXME: For non-element-backed pseudos this is still not quite
- // correct. For example for ::selection even though we invalidate
- // the style properly there's nothing that triggers a repaint
- // necessarily. Though this matches old Gecko behavior, and the
- // ::selection implementation needs to change significantly anyway
- // to implement https://github.com/w3c/csswg-drafts/issues/2474 for
- // example.
- invalidated_self = true;
- }
-
- debug!(
- " > Invalidation matched, next: {:?}, ({:?})",
- next_invalidation, next_combinator
- );
-
- let next_invalidation_kind = next_invalidation.kind();
-
- // We can skip pushing under some circumstances, and we should
- // because otherwise the invalidation list could grow
- // exponentially.
- //
- // * First of all, both invalidations need to be of the same
- // kind. This is because of how we propagate them going to
- // the right of the tree for sibling invalidations and going
- // down the tree for children invalidations. A sibling
- // invalidation that ends up generating a children
- // invalidation ends up (correctly) in five different lists,
- // not in the same list five different times.
- //
- // * Then, the invalidation needs to be matched by a previous
- // ancestor/sibling, in order to know that this invalidation
- // has been generated already.
- //
- // * Finally, the new invalidation needs to be
- // `effective_for_next()`, in order for us to know that it is
- // still in the list, since we remove the dependencies that
- // aren't from the lists for our children / siblings.
- //
- // To go through an example, let's imagine we are processing a
- // dom subtree like:
- //
- // <div><address><div><div/></div></address></div>
- //
- // And an invalidation list with a single invalidation like:
- //
- // [div div div]
- //
- // When we process the invalidation list for the outer div, we
- // match it, and generate a `div div` invalidation, so for the
- // <address> child we have:
- //
- // [div div div, div div]
- //
- // With the first of them marked as `matched`.
- //
- // When we process the <address> child, we don't match any of
- // them, so both invalidations go untouched to our children.
- //
- // When we process the second <div>, we match _both_
- // invalidations.
- //
- // However, when matching the first, we can tell it's been
- // matched, and not push the corresponding `div div`
- // invalidation, since we know it's necessarily already on the
- // list.
- //
- // Thus, without skipping the push, we'll arrive to the
- // innermost <div> with:
- //
- // [div div div, div div, div div, div]
- //
- // While skipping it, we won't arrive here with duplicating
- // dependencies:
- //
- // [div div div, div div, div]
- //
- let can_skip_pushing = next_invalidation_kind == invalidation_kind &&
- invalidation.matched_by_any_previous &&
- next_invalidation.effective_for_next();
-
- if can_skip_pushing {
- debug!(
- " > Can avoid push, since the invalidation had \
- already been matched before"
- );
- } else {
- match next_invalidation_kind {
- InvalidationKind::Descendant(DescendantInvalidationKind::Dom) => {
- descendant_invalidations
- .dom_descendants
- .push(next_invalidation);
- },
- InvalidationKind::Descendant(DescendantInvalidationKind::Part) => {
- descendant_invalidations.parts.push(next_invalidation);
- },
- InvalidationKind::Descendant(DescendantInvalidationKind::Slotted) => {
- descendant_invalidations
- .slotted_descendants
- .push(next_invalidation);
- },
- InvalidationKind::Sibling => {
- sibling_invalidations.push(next_invalidation);
- },
- }
- }
-
- SingleInvalidationResult {
- invalidated_self,
- matched: true,
- }
- }
-}
diff --git a/components/style/invalidation/element/mod.rs b/components/style/invalidation/element/mod.rs
deleted file mode 100644
index 1f19cc54f59..00000000000
--- a/components/style/invalidation/element/mod.rs
+++ /dev/null
@@ -1,12 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
-
-//! Invalidation of element styles due to attribute or style changes.
-
-pub mod document_state;
-pub mod element_wrapper;
-pub mod invalidation_map;
-pub mod invalidator;
-pub mod restyle_hints;
-pub mod state_and_attributes;
diff --git a/components/style/invalidation/element/restyle_hints.rs b/components/style/invalidation/element/restyle_hints.rs
deleted file mode 100644
index ffadecd7aa2..00000000000
--- a/components/style/invalidation/element/restyle_hints.rs
+++ /dev/null
@@ -1,190 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
-
-//! Restyle hints: an optimization to avoid unnecessarily matching selectors.
-
-use crate::traversal_flags::TraversalFlags;
-
-bitflags! {
- /// The kind of restyle we need to do for a given element.
- #[repr(C)]
- pub struct RestyleHint: u16 {
- /// Do a selector match of the element.
- const RESTYLE_SELF = 1 << 0;
-
- /// Do a selector match of the element's pseudo-elements. Always to be combined with
- /// RESTYLE_SELF.
- const RESTYLE_PSEUDOS = 1 << 1;
-
- /// Do a selector match if the element is a pseudo-element.
- const RESTYLE_SELF_IF_PSEUDO = 1 << 2;
-
- /// Do a selector match of the element's descendants.
- const RESTYLE_DESCENDANTS = 1 << 3;
-
- /// Recascade the current element.
- const RECASCADE_SELF = 1 << 4;
-
- /// Recascade the current element if it inherits any reset style.
- const RECASCADE_SELF_IF_INHERIT_RESET_STYLE = 1 << 5;
-
- /// Recascade all descendant elements.
- const RECASCADE_DESCENDANTS = 1 << 6;
-
- /// Replace the style data coming from CSS transitions without updating
- /// any other style data. This hint is only processed in animation-only
- /// traversal which is prior to normal traversal.
- const RESTYLE_CSS_TRANSITIONS = 1 << 7;
-
- /// Replace the style data coming from CSS animations without updating
- /// any other style data. This hint is only processed in animation-only
- /// traversal which is prior to normal traversal.
- const RESTYLE_CSS_ANIMATIONS = 1 << 8;
-
- /// Don't re-run selector-matching on the element, only the style
- /// attribute has changed, and this change didn't have any other
- /// dependencies.
- const RESTYLE_STYLE_ATTRIBUTE = 1 << 9;
-
- /// Replace the style data coming from SMIL animations without updating
- /// any other style data. This hint is only processed in animation-only
- /// traversal which is prior to normal traversal.
- const RESTYLE_SMIL = 1 << 10;
- }
-}
-
-impl RestyleHint {
- /// Creates a new `RestyleHint` indicating that the current element and all
- /// its descendants must be fully restyled.
- pub fn restyle_subtree() -> Self {
- RestyleHint::RESTYLE_SELF | RestyleHint::RESTYLE_DESCENDANTS
- }
-
- /// Creates a new `RestyleHint` indicating that the current element and all
- /// its descendants must be recascaded.
- pub fn recascade_subtree() -> Self {
- RestyleHint::RECASCADE_SELF | RestyleHint::RECASCADE_DESCENDANTS
- }
-
- /// Returns whether this hint invalidates the element and all its
- /// descendants.
- pub fn contains_subtree(&self) -> bool {
- self.contains(Self::restyle_subtree())
- }
-
- /// Returns whether we'll recascade all of the descendants.
- pub fn will_recascade_subtree(&self) -> bool {
- self.contains_subtree() || self.contains(Self::recascade_subtree())
- }
-
- /// Returns whether we need to restyle this element.
- pub fn has_non_animation_invalidations(&self) -> bool {
- !(*self & !Self::for_animations()).is_empty()
- }
-
- /// Propagates this restyle hint to a child element.
- pub fn propagate(&mut self, traversal_flags: &TraversalFlags) -> Self {
- use std::mem;
-
- // In the middle of an animation only restyle, we don't need to
- // propagate any restyle hints, and we need to remove ourselves.
- if traversal_flags.for_animation_only() {
- self.remove_animation_hints();
- return Self::empty();
- }
-
- debug_assert!(
- !self.has_animation_hint(),
- "There should not be any animation restyle hints \
- during normal traversal"
- );
-
- // Else we should clear ourselves, and return the propagated hint.
- mem::replace(self, Self::empty()).propagate_for_non_animation_restyle()
- }
-
- /// Returns a new `RestyleHint` appropriate for children of the current element.
- fn propagate_for_non_animation_restyle(&self) -> Self {
- if self.contains(RestyleHint::RESTYLE_DESCENDANTS) {
- return Self::restyle_subtree();
- }
- let mut result = Self::empty();
- if self.contains(RestyleHint::RESTYLE_PSEUDOS) {
- result |= Self::RESTYLE_SELF_IF_PSEUDO;
- }
- if self.contains(RestyleHint::RECASCADE_DESCENDANTS) {
- result |= Self::recascade_subtree();
- }
- result
- }
-
- /// Returns a hint that contains all the replacement hints.
- pub fn replacements() -> Self {
- RestyleHint::RESTYLE_STYLE_ATTRIBUTE | Self::for_animations()
- }
-
- /// The replacements for the animation cascade levels.
- #[inline]
- pub fn for_animations() -> Self {
- RestyleHint::RESTYLE_SMIL |
- RestyleHint::RESTYLE_CSS_ANIMATIONS |
- RestyleHint::RESTYLE_CSS_TRANSITIONS
- }
-
- /// Returns whether the hint specifies that an animation cascade level must
- /// be replaced.
- #[inline]
- pub fn has_animation_hint(&self) -> bool {
- self.intersects(Self::for_animations())
- }
-
- /// Returns whether the hint specifies that an animation cascade level must
- /// be replaced.
- #[inline]
- pub fn has_animation_hint_or_recascade(&self) -> bool {
- self.intersects(
- Self::for_animations() |
- Self::RECASCADE_SELF |
- Self::RECASCADE_SELF_IF_INHERIT_RESET_STYLE,
- )
- }
-
- /// Returns whether the hint specifies some restyle work other than an
- /// animation cascade level replacement.
- #[inline]
- pub fn has_non_animation_hint(&self) -> bool {
- !(*self & !Self::for_animations()).is_empty()
- }
-
- /// Returns whether the hint specifies that some cascade levels must be
- /// replaced.
- #[inline]
- pub fn has_replacements(&self) -> bool {
- self.intersects(Self::replacements())
- }
-
- /// Removes all of the animation-related hints.
- #[inline]
- pub fn remove_animation_hints(&mut self) {
- self.remove(Self::for_animations());
-
- // While RECASCADE_SELF is not animation-specific, we only ever add and process it during
- // traversal. If we are here, removing animation hints, then we are in an animation-only
- // traversal, and we know that any RECASCADE_SELF flag must have been set due to changes in
- // inherited values after restyling for animations, and thus we want to remove it so that
- // we don't later try to restyle the element during a normal restyle.
- // (We could have separate RECASCADE_SELF_NORMAL and RECASCADE_SELF_ANIMATIONS flags to
- // make it clear, but this isn't currently necessary.)
- self.remove(Self::RECASCADE_SELF | Self::RECASCADE_SELF_IF_INHERIT_RESET_STYLE);
- }
-}
-
-impl Default for RestyleHint {
- fn default() -> Self {
- Self::empty()
- }
-}
-
-#[cfg(feature = "servo")]
-malloc_size_of_is_0!(RestyleHint);
diff --git a/components/style/invalidation/element/state_and_attributes.rs b/components/style/invalidation/element/state_and_attributes.rs
deleted file mode 100644
index bd69f35c66c..00000000000
--- a/components/style/invalidation/element/state_and_attributes.rs
+++ /dev/null
@@ -1,552 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
-
-//! An invalidation processor for style changes due to state and attribute
-//! changes.
-
-use crate::context::SharedStyleContext;
-use crate::data::ElementData;
-use crate::dom::{TElement, TNode};
-use crate::invalidation::element::element_wrapper::{ElementSnapshot, ElementWrapper};
-use crate::invalidation::element::invalidation_map::*;
-use crate::invalidation::element::invalidator::{DescendantInvalidationLists, InvalidationVector};
-use crate::invalidation::element::invalidator::{Invalidation, InvalidationProcessor};
-use crate::invalidation::element::restyle_hints::RestyleHint;
-use crate::selector_map::SelectorMap;
-use crate::selector_parser::Snapshot;
-use crate::stylesheets::origin::OriginSet;
-use crate::{Atom, WeakAtom};
-use selectors::attr::CaseSensitivity;
-use selectors::matching::{
- matches_selector, MatchingContext, MatchingMode, NeedsSelectorFlags, VisitedHandlingMode,
-};
-use selectors::NthIndexCache;
-use smallvec::SmallVec;
-use style_traits::dom::ElementState;
-
-/// The collector implementation.
-struct Collector<'a, 'b: 'a, 'selectors: 'a, E>
-where
- E: TElement,
-{
- element: E,
- wrapper: ElementWrapper<'b, E>,
- snapshot: &'a Snapshot,
- matching_context: &'a mut MatchingContext<'b, E::Impl>,
- lookup_element: E,
- removed_id: Option<&'a WeakAtom>,
- added_id: Option<&'a WeakAtom>,
- classes_removed: &'a SmallVec<[Atom; 8]>,
- classes_added: &'a SmallVec<[Atom; 8]>,
- state_changes: ElementState,
- descendant_invalidations: &'a mut DescendantInvalidationLists<'selectors>,
- sibling_invalidations: &'a mut InvalidationVector<'selectors>,
- invalidates_self: bool,
-}
-
-/// An invalidation processor for style changes due to state and attribute
-/// changes.
-pub struct StateAndAttrInvalidationProcessor<'a, 'b: 'a, E: TElement> {
- shared_context: &'a SharedStyleContext<'b>,
- element: E,
- data: &'a mut ElementData,
- matching_context: MatchingContext<'a, E::Impl>,
-}
-
-impl<'a, 'b: 'a, E: TElement + 'b> StateAndAttrInvalidationProcessor<'a, 'b, E> {
- /// Creates a new StateAndAttrInvalidationProcessor.
- pub fn new(
- shared_context: &'a SharedStyleContext<'b>,
- element: E,
- data: &'a mut ElementData,
- nth_index_cache: &'a mut NthIndexCache,
- ) -> Self {
- let matching_context = MatchingContext::new_for_visited(
- MatchingMode::Normal,
- None,
- nth_index_cache,
- VisitedHandlingMode::AllLinksVisitedAndUnvisited,
- shared_context.quirks_mode(),
- NeedsSelectorFlags::No,
- );
-
- Self {
- shared_context,
- element,
- data,
- matching_context,
- }
- }
-}
-
-/// Checks a dependency against a given element and wrapper, to see if something
-/// changed.
-pub fn check_dependency<E, W>(
- dependency: &Dependency,
- element: &E,
- wrapper: &W,
- context: &mut MatchingContext<'_, E::Impl>,
-) -> bool
-where
- E: TElement,
- W: selectors::Element<Impl = E::Impl>,
-{
- let matches_now = matches_selector(
- &dependency.selector,
- dependency.selector_offset,
- None,
- element,
- context,
- );
-
- let matched_then = matches_selector(
- &dependency.selector,
- dependency.selector_offset,
- None,
- wrapper,
- context,
- );
-
- matched_then != matches_now
-}
-
-/// Whether we should process the descendants of a given element for style
-/// invalidation.
-pub fn should_process_descendants(data: &ElementData) -> bool {
- !data.styles.is_display_none() && !data.hint.contains(RestyleHint::RESTYLE_DESCENDANTS)
-}
-
-/// Propagates the bits after invalidating a descendant child.
-pub fn propagate_dirty_bit_up_to<E>(ancestor: E, child: E)
-where
- E: TElement,
-{
- // The child may not be a flattened tree child of the current element,
- // but may be arbitrarily deep.
- //
- // Since we keep the traversal flags in terms of the flattened tree,
- // we need to propagate it as appropriate.
- let mut current = child.traversal_parent();
- while let Some(parent) = current.take() {
- unsafe { parent.set_dirty_descendants() };
- current = parent.traversal_parent();
-
- if parent == ancestor {
- return;
- }
- }
- debug_assert!(
- false,
- "Should've found {:?} as an ancestor of {:?}",
- ancestor, child
- );
-}
-
-/// Propagates the bits after invalidating a descendant child, if needed.
-pub fn invalidated_descendants<E>(element: E, child: E)
-where
- E: TElement,
-{
- if !child.has_data() {
- return;
- }
- propagate_dirty_bit_up_to(element, child)
-}
-
-/// Sets the appropriate restyle hint after invalidating the style of a given
-/// element.
-pub fn invalidated_self<E>(element: E) -> bool
-where
- E: TElement,
-{
- let mut data = match element.mutate_data() {
- Some(data) => data,
- None => return false,
- };
- data.hint.insert(RestyleHint::RESTYLE_SELF);
- true
-}
-
-/// Sets the appropriate hint after invalidating the style of a sibling.
-pub fn invalidated_sibling<E>(element: E, of: E)
-where
- E: TElement,
-{
- debug_assert_eq!(
- element.as_node().parent_node(),
- of.as_node().parent_node(),
- "Should be siblings"
- );
- if !invalidated_self(element) {
- return;
- }
- if element.traversal_parent() != of.traversal_parent() {
- let parent = element.as_node().parent_element_or_host();
- debug_assert!(
- parent.is_some(),
- "How can we have siblings without parent nodes?"
- );
- if let Some(e) = parent {
- propagate_dirty_bit_up_to(e, element)
- }
- }
-}
-
-impl<'a, 'b: 'a, E: 'a> InvalidationProcessor<'a, E>
- for StateAndAttrInvalidationProcessor<'a, 'b, E>
-where
- E: TElement,
-{
- /// We need to invalidate style on pseudo-elements, in order to process
- /// changes that could otherwise end up in ::before or ::after content being
- /// generated, and invalidate lazy pseudo caches.
- fn invalidates_on_pseudo_element(&self) -> bool {
- true
- }
-
- fn check_outer_dependency(&mut self, dependency: &Dependency, element: E) -> bool {
- // We cannot assert about `element` having a snapshot here (in fact it
- // most likely won't), because it may be an arbitrary descendant or
- // later-sibling of the element we started invalidating with.
- let wrapper = ElementWrapper::new(element, &*self.shared_context.snapshot_map);
- check_dependency(dependency, &element, &wrapper, &mut self.matching_context)
- }
-
- fn matching_context(&mut self) -> &mut MatchingContext<'a, E::Impl> {
- &mut self.matching_context
- }
-
- fn collect_invalidations(
- &mut self,
- element: E,
- _self_invalidations: &mut InvalidationVector<'a>,
- descendant_invalidations: &mut DescendantInvalidationLists<'a>,
- sibling_invalidations: &mut InvalidationVector<'a>,
- ) -> bool {
- debug_assert_eq!(element, self.element);
- debug_assert!(element.has_snapshot(), "Why bothering?");
-
- let wrapper = ElementWrapper::new(element, &*self.shared_context.snapshot_map);
-
- let state_changes = wrapper.state_changes();
- let snapshot = wrapper.snapshot().expect("has_snapshot lied");
-
- if !snapshot.has_attrs() && state_changes.is_empty() {
- return false;
- }
-
- let mut classes_removed = SmallVec::<[Atom; 8]>::new();
- let mut classes_added = SmallVec::<[Atom; 8]>::new();
- if snapshot.class_changed() {
- // TODO(emilio): Do this more efficiently!
- snapshot.each_class(|c| {
- if !element.has_class(c, CaseSensitivity::CaseSensitive) {
- classes_removed.push(c.0.clone())
- }
- });
-
- element.each_class(|c| {
- if !snapshot.has_class(c, CaseSensitivity::CaseSensitive) {
- classes_added.push(c.0.clone())
- }
- })
- }
-
- let mut id_removed = None;
- let mut id_added = None;
- if snapshot.id_changed() {
- let old_id = snapshot.id_attr();
- let current_id = element.id();
-
- if old_id != current_id {
- id_removed = old_id;
- id_added = current_id;
- }
- }
-
- if log_enabled!(::log::Level::Debug) {
- debug!("Collecting changes for: {:?}", element);
- if !state_changes.is_empty() {
- debug!(" > state: {:?}", state_changes);
- }
- if snapshot.id_changed() {
- debug!(" > id changed: +{:?} -{:?}", id_added, id_removed);
- }
- if snapshot.class_changed() {
- debug!(
- " > class changed: +{:?} -{:?}",
- classes_added, classes_removed
- );
- }
- let mut attributes_changed = false;
- snapshot.each_attr_changed(|_| {
- attributes_changed = true;
- });
- if attributes_changed {
- debug!(
- " > attributes changed, old: {}",
- snapshot.debug_list_attributes()
- )
- }
- }
-
- let lookup_element = if element.implemented_pseudo_element().is_some() {
- element.pseudo_element_originating_element().unwrap()
- } else {
- element
- };
-
- let mut shadow_rule_datas = SmallVec::<[_; 3]>::new();
- let matches_document_author_rules =
- element.each_applicable_non_document_style_rule_data(|data, host| {
- shadow_rule_datas.push((data, host.opaque()))
- });
-
- let invalidated_self = {
- let mut collector = Collector {
- wrapper,
- lookup_element,
- state_changes,
- element,
- snapshot: &snapshot,
- matching_context: &mut self.matching_context,
- removed_id: id_removed,
- added_id: id_added,
- classes_removed: &classes_removed,
- classes_added: &classes_added,
- descendant_invalidations,
- sibling_invalidations,
- invalidates_self: false,
- };
-
- let document_origins = if !matches_document_author_rules {
- OriginSet::ORIGIN_USER_AGENT | OriginSet::ORIGIN_USER
- } else {
- OriginSet::all()
- };
-
- for (cascade_data, origin) in self.shared_context.stylist.iter_origins() {
- if document_origins.contains(origin.into()) {
- collector
- .collect_dependencies_in_invalidation_map(cascade_data.invalidation_map());
- }
- }
-
- for &(ref data, ref host) in &shadow_rule_datas {
- collector.matching_context.current_host = Some(host.clone());
- collector.collect_dependencies_in_invalidation_map(data.invalidation_map());
- }
-
- collector.invalidates_self
- };
-
- // If we generated a ton of descendant invalidations, it's probably not
- // worth to go ahead and try to process them.
- //
- // Just restyle the descendants directly.
- //
- // This number is completely made-up, but the page that made us add this
- // code generated 1960+ invalidations (bug 1420741).
- //
- // We don't look at slotted_descendants because those don't propagate
- // down more than one level anyway.
- if descendant_invalidations.dom_descendants.len() > 150 {
- self.data.hint.insert(RestyleHint::RESTYLE_DESCENDANTS);
- }
-
- if invalidated_self {
- self.data.hint.insert(RestyleHint::RESTYLE_SELF);
- }
-
- invalidated_self
- }
-
- fn should_process_descendants(&mut self, element: E) -> bool {
- if element == self.element {
- return should_process_descendants(&self.data);
- }
-
- match element.borrow_data() {
- Some(d) => should_process_descendants(&d),
- None => return false,
- }
- }
-
- fn recursion_limit_exceeded(&mut self, element: E) {
- if element == self.element {
- self.data.hint.insert(RestyleHint::RESTYLE_DESCENDANTS);
- return;
- }
-
- if let Some(mut data) = element.mutate_data() {
- data.hint.insert(RestyleHint::RESTYLE_DESCENDANTS);
- }
- }
-
- fn invalidated_descendants(&mut self, element: E, child: E) {
- invalidated_descendants(element, child)
- }
-
- fn invalidated_self(&mut self, element: E) {
- debug_assert_ne!(element, self.element);
- invalidated_self(element);
- }
-
- fn invalidated_sibling(&mut self, element: E, of: E) {
- debug_assert_ne!(element, self.element);
- invalidated_sibling(element, of);
- }
-}
-
-impl<'a, 'b, 'selectors, E> Collector<'a, 'b, 'selectors, E>
-where
- E: TElement,
- 'selectors: 'a,
-{
- fn collect_dependencies_in_invalidation_map(&mut self, map: &'selectors InvalidationMap) {
- let quirks_mode = self.matching_context.quirks_mode();
- let removed_id = self.removed_id;
- if let Some(ref id) = removed_id {
- if let Some(deps) = map.id_to_selector.get(id, quirks_mode) {
- for dep in deps {
- self.scan_dependency(dep);
- }
- }
- }
-
- let added_id = self.added_id;
- if let Some(ref id) = added_id {
- if let Some(deps) = map.id_to_selector.get(id, quirks_mode) {
- for dep in deps {
- self.scan_dependency(dep);
- }
- }
- }
-
- for class in self.classes_added.iter().chain(self.classes_removed.iter()) {
- if let Some(deps) = map.class_to_selector.get(class, quirks_mode) {
- for dep in deps {
- self.scan_dependency(dep);
- }
- }
- }
-
- self.snapshot.each_attr_changed(|attribute| {
- if let Some(deps) = map.other_attribute_affecting_selectors.get(attribute) {
- for dep in deps {
- self.scan_dependency(dep);
- }
- }
- });
-
- self.collect_state_dependencies(&map.state_affecting_selectors)
- }
-
- fn collect_state_dependencies(&mut self, map: &'selectors SelectorMap<StateDependency>) {
- if self.state_changes.is_empty() {
- return;
- }
- map.lookup_with_additional(
- self.lookup_element,
- self.matching_context.quirks_mode(),
- self.removed_id,
- self.classes_removed,
- self.state_changes,
- |dependency| {
- if !dependency.state.intersects(self.state_changes) {
- return true;
- }
- self.scan_dependency(&dependency.dep);
- true
- },
- );
- }
-
- /// Check whether a dependency should be taken into account.
- #[inline]
- fn check_dependency(&mut self, dependency: &Dependency) -> bool {
- check_dependency(
- dependency,
- &self.element,
- &self.wrapper,
- &mut self.matching_context,
- )
- }
-
- fn scan_dependency(&mut self, dependency: &'selectors Dependency) {
- debug!(
- "TreeStyleInvalidator::scan_dependency({:?}, {:?})",
- self.element, dependency
- );
-
- if !self.dependency_may_be_relevant(dependency) {
- return;
- }
-
- if self.check_dependency(dependency) {
- return self.note_dependency(dependency);
- }
- }
-
- fn note_dependency(&mut self, dependency: &'selectors Dependency) {
- debug_assert!(self.dependency_may_be_relevant(dependency));
-
- let invalidation_kind = dependency.invalidation_kind();
- if matches!(invalidation_kind, DependencyInvalidationKind::Element) {
- if let Some(ref parent) = dependency.parent {
- // We know something changed in the inner selector, go outwards
- // now.
- self.scan_dependency(parent);
- } else {
- self.invalidates_self = true;
- }
- return;
- }
-
- debug_assert_ne!(dependency.selector_offset, 0);
- debug_assert_ne!(dependency.selector_offset, dependency.selector.len());
-
- let invalidation =
- Invalidation::new(&dependency, self.matching_context.current_host.clone());
-
- match invalidation_kind {
- DependencyInvalidationKind::Element => unreachable!(),
- DependencyInvalidationKind::ElementAndDescendants => {
- self.invalidates_self = true;
- self.descendant_invalidations
- .dom_descendants
- .push(invalidation);
- },
- DependencyInvalidationKind::Descendants => {
- self.descendant_invalidations
- .dom_descendants
- .push(invalidation);
- },
- DependencyInvalidationKind::Siblings => {
- self.sibling_invalidations.push(invalidation);
- },
- DependencyInvalidationKind::Parts => {
- self.descendant_invalidations.parts.push(invalidation);
- },
- DependencyInvalidationKind::SlottedElements => {
- self.descendant_invalidations
- .slotted_descendants
- .push(invalidation);
- },
- }
- }
-
- /// Returns whether `dependency` may cause us to invalidate the style of
- /// more elements than what we've already invalidated.
- fn dependency_may_be_relevant(&self, dependency: &Dependency) -> bool {
- match dependency.invalidation_kind() {
- DependencyInvalidationKind::Element => !self.invalidates_self,
- DependencyInvalidationKind::SlottedElements => self.element.is_html_slot_element(),
- DependencyInvalidationKind::Parts => self.element.shadow_root().is_some(),
- DependencyInvalidationKind::ElementAndDescendants |
- DependencyInvalidationKind::Siblings |
- DependencyInvalidationKind::Descendants => true,
- }
- }
-}
diff --git a/components/style/invalidation/media_queries.rs b/components/style/invalidation/media_queries.rs
deleted file mode 100644
index 6928b29d3d9..00000000000
--- a/components/style/invalidation/media_queries.rs
+++ /dev/null
@@ -1,130 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
-
-//! Code related to the invalidation of media-query-affected rules.
-
-use crate::context::QuirksMode;
-use crate::media_queries::Device;
-use crate::shared_lock::SharedRwLockReadGuard;
-use crate::stylesheets::{DocumentRule, ImportRule, MediaRule};
-use crate::stylesheets::{NestedRuleIterationCondition, StylesheetContents, SupportsRule};
-use fxhash::FxHashSet;
-
-/// A key for a given media query result.
-///
-/// NOTE: It happens to be the case that all the media lists we care about
-/// happen to have a stable address, so we can just use an opaque pointer to
-/// represent them.
-///
-/// Also, note that right now when a rule or stylesheet is removed, we do a full
-/// style flush, so there's no need to worry about other item created with the
-/// same pointer address.
-///
-/// If this changes, though, we may need to remove the item from the cache if
-/// present before it goes away.
-#[derive(Clone, Copy, Debug, Eq, Hash, MallocSizeOf, PartialEq)]
-pub struct MediaListKey(usize);
-
-impl MediaListKey {
- /// Create a MediaListKey from a raw usize.
- pub fn from_raw(k: usize) -> Self {
- MediaListKey(k)
- }
-}
-
-/// A trait to get a given `MediaListKey` for a given item that can hold a
-/// `MediaList`.
-pub trait ToMediaListKey: Sized {
- /// Get a `MediaListKey` for this item. This key needs to uniquely identify
- /// the item.
- fn to_media_list_key(&self) -> MediaListKey {
- MediaListKey(self as *const Self as usize)
- }
-}
-
-impl ToMediaListKey for StylesheetContents {}
-impl ToMediaListKey for ImportRule {}
-impl ToMediaListKey for MediaRule {}
-
-/// A struct that holds the result of a media query evaluation pass for the
-/// media queries that evaluated successfully.
-#[derive(Clone, Debug, MallocSizeOf, PartialEq)]
-pub struct EffectiveMediaQueryResults {
- /// The set of media lists that matched last time.
- set: FxHashSet<MediaListKey>,
-}
-
-impl EffectiveMediaQueryResults {
- /// Trivially constructs an empty `EffectiveMediaQueryResults`.
- pub fn new() -> Self {
- Self {
- set: FxHashSet::default(),
- }
- }
-
- /// Resets the results, using an empty key.
- pub fn clear(&mut self) {
- self.set.clear()
- }
-
- /// Returns whether a given item was known to be effective when the results
- /// were cached.
- pub fn was_effective<T>(&self, item: &T) -> bool
- where
- T: ToMediaListKey,
- {
- self.set.contains(&item.to_media_list_key())
- }
-
- /// Notices that an effective item has been seen, and caches it as matching.
- pub fn saw_effective<T>(&mut self, item: &T)
- where
- T: ToMediaListKey,
- {
- // NOTE(emilio): We can't assert that we don't cache the same item twice
- // because of stylesheet reusing... shrug.
- self.set.insert(item.to_media_list_key());
- }
-}
-
-/// A filter that filters over effective rules, but allowing all potentially
-/// effective `@media` rules.
-pub struct PotentiallyEffectiveMediaRules;
-
-impl NestedRuleIterationCondition for PotentiallyEffectiveMediaRules {
- fn process_import(
- _: &SharedRwLockReadGuard,
- _: &Device,
- _: QuirksMode,
- _: &ImportRule,
- ) -> bool {
- true
- }
-
- fn process_media(_: &SharedRwLockReadGuard, _: &Device, _: QuirksMode, _: &MediaRule) -> bool {
- true
- }
-
- /// Whether we should process the nested rules in a given `@-moz-document` rule.
- fn process_document(
- guard: &SharedRwLockReadGuard,
- device: &Device,
- quirks_mode: QuirksMode,
- rule: &DocumentRule,
- ) -> bool {
- use crate::stylesheets::EffectiveRules;
- EffectiveRules::process_document(guard, device, quirks_mode, rule)
- }
-
- /// Whether we should process the nested rules in a given `@supports` rule.
- fn process_supports(
- guard: &SharedRwLockReadGuard,
- device: &Device,
- quirks_mode: QuirksMode,
- rule: &SupportsRule,
- ) -> bool {
- use crate::stylesheets::EffectiveRules;
- EffectiveRules::process_supports(guard, device, quirks_mode, rule)
- }
-}
diff --git a/components/style/invalidation/mod.rs b/components/style/invalidation/mod.rs
deleted file mode 100644
index 12b0d06853b..00000000000
--- a/components/style/invalidation/mod.rs
+++ /dev/null
@@ -1,10 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
-
-//! Different bits of code related to invalidating style.
-
-pub mod element;
-pub mod media_queries;
-pub mod stylesheets;
-pub mod viewport_units;
diff --git a/components/style/invalidation/stylesheets.rs b/components/style/invalidation/stylesheets.rs
deleted file mode 100644
index 6ae67452dbd..00000000000
--- a/components/style/invalidation/stylesheets.rs
+++ /dev/null
@@ -1,655 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
-
-//! A collection of invalidations due to changes in which stylesheets affect a
-//! document.
-
-#![deny(unsafe_code)]
-
-use crate::context::QuirksMode;
-use crate::dom::{TDocument, TElement, TNode};
-use crate::invalidation::element::element_wrapper::{ElementSnapshot, ElementWrapper};
-use crate::invalidation::element::restyle_hints::RestyleHint;
-use crate::media_queries::Device;
-use crate::selector_map::{MaybeCaseInsensitiveHashMap, PrecomputedHashMap};
-use crate::selector_parser::{SelectorImpl, Snapshot, SnapshotMap};
-use crate::shared_lock::SharedRwLockReadGuard;
-use crate::stylesheets::{CssRule, StylesheetInDocument};
-use crate::stylesheets::{EffectiveRules, EffectiveRulesIterator};
-use crate::values::AtomIdent;
-use crate::LocalName as SelectorLocalName;
-use crate::{Atom, ShrinkIfNeeded};
-use selectors::parser::{Component, LocalName, Selector};
-
-/// The kind of change that happened for a given rule.
-#[repr(u32)]
-#[derive(Clone, Copy, Debug, Eq, Hash, MallocSizeOf, PartialEq)]
-pub enum RuleChangeKind {
- /// The rule was inserted.
- Insertion,
- /// The rule was removed.
- Removal,
- /// Some change in the rule which we don't know about, and could have made
- /// the rule change in any way.
- Generic,
- /// A change in the declarations of a style rule.
- StyleRuleDeclarations,
-}
-
-/// A style sheet invalidation represents a kind of element or subtree that may
-/// need to be restyled. Whether it represents a whole subtree or just a single
-/// element is determined by the given InvalidationKind in
-/// StylesheetInvalidationSet's maps.
-#[derive(Debug, Eq, Hash, MallocSizeOf, PartialEq)]
-enum Invalidation {
- /// An element with a given id.
- ID(AtomIdent),
- /// An element with a given class name.
- Class(AtomIdent),
- /// An element with a given local name.
- LocalName {
- name: SelectorLocalName,
- lower_name: SelectorLocalName,
- },
-}
-
-impl Invalidation {
- fn is_id(&self) -> bool {
- matches!(*self, Invalidation::ID(..))
- }
-
- fn is_id_or_class(&self) -> bool {
- matches!(*self, Invalidation::ID(..) | Invalidation::Class(..))
- }
-}
-
-/// Whether we should invalidate just the element, or the whole subtree within
-/// it.
-#[derive(Clone, Copy, Debug, Eq, MallocSizeOf, Ord, PartialEq, PartialOrd)]
-enum InvalidationKind {
- None = 0,
- Element,
- Scope,
-}
-
-impl std::ops::BitOrAssign for InvalidationKind {
- #[inline]
- fn bitor_assign(&mut self, other: Self) {
- *self = std::cmp::max(*self, other);
- }
-}
-
-impl InvalidationKind {
- #[inline]
- fn is_scope(self) -> bool {
- matches!(self, Self::Scope)
- }
-
- #[inline]
- fn add(&mut self, other: Option<&InvalidationKind>) {
- if let Some(other) = other {
- *self |= *other;
- }
- }
-}
-
-/// A set of invalidations due to stylesheet additions.
-///
-/// TODO(emilio): We might be able to do the same analysis for media query
-/// changes too (or even selector changes?).
-#[derive(Debug, Default, MallocSizeOf)]
-pub struct StylesheetInvalidationSet {
- classes: MaybeCaseInsensitiveHashMap<Atom, InvalidationKind>,
- ids: MaybeCaseInsensitiveHashMap<Atom, InvalidationKind>,
- local_names: PrecomputedHashMap<SelectorLocalName, InvalidationKind>,
- fully_invalid: bool,
-}
-
-impl StylesheetInvalidationSet {
- /// Create an empty `StylesheetInvalidationSet`.
- pub fn new() -> Self {
- Default::default()
- }
-
- /// Mark the DOM tree styles' as fully invalid.
- pub fn invalidate_fully(&mut self) {
- debug!("StylesheetInvalidationSet::invalidate_fully");
- self.clear();
- self.fully_invalid = true;
- }
-
- fn shrink_if_needed(&mut self) {
- if self.fully_invalid {
- return;
- }
- self.classes.shrink_if_needed();
- self.ids.shrink_if_needed();
- self.local_names.shrink_if_needed();
- }
-
- /// Analyze the given stylesheet, and collect invalidations from their
- /// rules, in order to avoid doing a full restyle when we style the document
- /// next time.
- pub fn collect_invalidations_for<S>(
- &mut self,
- device: &Device,
- stylesheet: &S,
- guard: &SharedRwLockReadGuard,
- ) where
- S: StylesheetInDocument,
- {
- debug!("StylesheetInvalidationSet::collect_invalidations_for");
- if self.fully_invalid {
- debug!(" > Fully invalid already");
- return;
- }
-
- if !stylesheet.enabled() || !stylesheet.is_effective_for_device(device, guard) {
- debug!(" > Stylesheet was not effective");
- return; // Nothing to do here.
- }
-
- let quirks_mode = device.quirks_mode();
- for rule in stylesheet.effective_rules(device, guard) {
- self.collect_invalidations_for_rule(rule, guard, device, quirks_mode);
- if self.fully_invalid {
- break;
- }
- }
-
- self.shrink_if_needed();
-
- debug!(" > resulting class invalidations: {:?}", self.classes);
- debug!(" > resulting id invalidations: {:?}", self.ids);
- debug!(
- " > resulting local name invalidations: {:?}",
- self.local_names
- );
- debug!(" > fully_invalid: {}", self.fully_invalid);
- }
-
- /// Clears the invalidation set, invalidating elements as needed if
- /// `document_element` is provided.
- ///
- /// Returns true if any invalidations ocurred.
- pub fn flush<E>(&mut self, document_element: Option<E>, snapshots: Option<&SnapshotMap>) -> bool
- where
- E: TElement,
- {
- debug!(
- "Stylist::flush({:?}, snapshots: {})",
- document_element,
- snapshots.is_some()
- );
- let have_invalidations = match document_element {
- Some(e) => self.process_invalidations(e, snapshots),
- None => false,
- };
- self.clear();
- have_invalidations
- }
-
- /// Returns whether there's no invalidation to process.
- pub fn is_empty(&self) -> bool {
- !self.fully_invalid &&
- self.classes.is_empty() &&
- self.ids.is_empty() &&
- self.local_names.is_empty()
- }
-
- fn invalidation_kind_for<E>(
- &self,
- element: E,
- snapshot: Option<&Snapshot>,
- quirks_mode: QuirksMode,
- ) -> InvalidationKind
- where
- E: TElement,
- {
- debug_assert!(!self.fully_invalid);
-
- let mut kind = InvalidationKind::None;
-
- if !self.classes.is_empty() {
- element.each_class(|c| {
- kind.add(self.classes.get(c, quirks_mode));
- });
-
- if kind.is_scope() {
- return kind;
- }
-
- if let Some(snapshot) = snapshot {
- snapshot.each_class(|c| {
- kind.add(self.classes.get(c, quirks_mode));
- });
-
- if kind.is_scope() {
- return kind;
- }
- }
- }
-
- if !self.ids.is_empty() {
- if let Some(ref id) = element.id() {
- kind.add(self.ids.get(id, quirks_mode));
- if kind.is_scope() {
- return kind;
- }
- }
-
- if let Some(ref old_id) = snapshot.and_then(|s| s.id_attr()) {
- kind.add(self.ids.get(old_id, quirks_mode));
- if kind.is_scope() {
- return kind;
- }
- }
- }
-
- if !self.local_names.is_empty() {
- kind.add(self.local_names.get(element.local_name()));
- }
-
- kind
- }
-
- /// Clears the invalidation set without processing.
- pub fn clear(&mut self) {
- self.classes.clear();
- self.ids.clear();
- self.local_names.clear();
- self.fully_invalid = false;
- debug_assert!(self.is_empty());
- }
-
- fn process_invalidations<E>(&self, element: E, snapshots: Option<&SnapshotMap>) -> bool
- where
- E: TElement,
- {
- debug!("Stylist::process_invalidations({:?}, {:?})", element, self);
-
- {
- let mut data = match element.mutate_data() {
- Some(data) => data,
- None => return false,
- };
-
- if self.fully_invalid {
- debug!("process_invalidations: fully_invalid({:?})", element);
- data.hint.insert(RestyleHint::restyle_subtree());
- return true;
- }
- }
-
- if self.is_empty() {
- debug!("process_invalidations: empty invalidation set");
- return false;
- }
-
- let quirks_mode = element.as_node().owner_doc().quirks_mode();
- self.process_invalidations_in_subtree(element, snapshots, quirks_mode)
- }
-
- /// Process style invalidations in a given subtree. This traverses the
- /// subtree looking for elements that match the invalidations in our hash
- /// map members.
- ///
- /// Returns whether it invalidated at least one element's style.
- #[allow(unsafe_code)]
- fn process_invalidations_in_subtree<E>(
- &self,
- element: E,
- snapshots: Option<&SnapshotMap>,
- quirks_mode: QuirksMode,
- ) -> bool
- where
- E: TElement,
- {
- debug!("process_invalidations_in_subtree({:?})", element);
- let mut data = match element.mutate_data() {
- Some(data) => data,
- None => return false,
- };
-
- if !data.has_styles() {
- return false;
- }
-
- if data.hint.contains_subtree() {
- debug!(
- "process_invalidations_in_subtree: {:?} was already invalid",
- element
- );
- return false;
- }
-
- let element_wrapper = snapshots.map(|s| ElementWrapper::new(element, s));
- let snapshot = element_wrapper.as_ref().and_then(|e| e.snapshot());
-
- match self.invalidation_kind_for(element, snapshot, quirks_mode) {
- InvalidationKind::None => {},
- InvalidationKind::Element => {
- debug!(
- "process_invalidations_in_subtree: {:?} matched self",
- element
- );
- data.hint.insert(RestyleHint::RESTYLE_SELF);
- },
- InvalidationKind::Scope => {
- debug!(
- "process_invalidations_in_subtree: {:?} matched subtree",
- element
- );
- data.hint.insert(RestyleHint::restyle_subtree());
- return true;
- },
- }
-
- let mut any_children_invalid = false;
-
- for child in element.traversal_children() {
- let child = match child.as_element() {
- Some(e) => e,
- None => continue,
- };
-
- any_children_invalid |=
- self.process_invalidations_in_subtree(child, snapshots, quirks_mode);
- }
-
- if any_children_invalid {
- debug!(
- "Children of {:?} changed, setting dirty descendants",
- element
- );
- unsafe { element.set_dirty_descendants() }
- }
-
- data.hint.contains(RestyleHint::RESTYLE_SELF) || any_children_invalid
- }
-
- /// TODO(emilio): Reuse the bucket stuff from selectormap? That handles
- /// :is() / :where() etc.
- fn scan_component(
- component: &Component<SelectorImpl>,
- invalidation: &mut Option<Invalidation>,
- ) {
- match *component {
- Component::LocalName(LocalName {
- ref name,
- ref lower_name,
- }) => {
- if invalidation.is_none() {
- *invalidation = Some(Invalidation::LocalName {
- name: name.clone(),
- lower_name: lower_name.clone(),
- });
- }
- },
- Component::Class(ref class) => {
- if invalidation.as_ref().map_or(true, |s| !s.is_id_or_class()) {
- *invalidation = Some(Invalidation::Class(class.clone()));
- }
- },
- Component::ID(ref id) => {
- if invalidation.as_ref().map_or(true, |s| !s.is_id()) {
- *invalidation = Some(Invalidation::ID(id.clone()));
- }
- },
- _ => {
- // Ignore everything else, at least for now.
- },
- }
- }
-
- /// Collect invalidations for a given selector.
- ///
- /// We look at the outermost local name, class, or ID selector to the left
- /// of an ancestor combinator, in order to restyle only a given subtree.
- ///
- /// If the selector has no ancestor combinator, then we do the same for
- /// the only sequence it has, but record it as an element invalidation
- /// instead of a subtree invalidation.
- ///
- /// We prefer IDs to classs, and classes to local names, on the basis
- /// that the former should be more specific than the latter. We also
- /// prefer to generate subtree invalidations for the outermost part
- /// of the selector, to reduce the amount of traversal we need to do
- /// when flushing invalidations.
- fn collect_invalidations(
- &mut self,
- selector: &Selector<SelectorImpl>,
- quirks_mode: QuirksMode,
- ) {
- debug!(
- "StylesheetInvalidationSet::collect_invalidations({:?})",
- selector
- );
-
- let mut element_invalidation: Option<Invalidation> = None;
- let mut subtree_invalidation: Option<Invalidation> = None;
-
- let mut scan_for_element_invalidation = true;
- let mut scan_for_subtree_invalidation = false;
-
- let mut iter = selector.iter();
-
- loop {
- for component in &mut iter {
- if scan_for_element_invalidation {
- Self::scan_component(component, &mut element_invalidation);
- } else if scan_for_subtree_invalidation {
- Self::scan_component(component, &mut subtree_invalidation);
- }
- }
- match iter.next_sequence() {
- None => break,
- Some(combinator) => {
- scan_for_subtree_invalidation = combinator.is_ancestor();
- },
- }
- scan_for_element_invalidation = false;
- }
-
- if let Some(s) = subtree_invalidation {
- debug!(" > Found subtree invalidation: {:?}", s);
- if self.insert_invalidation(s, InvalidationKind::Scope, quirks_mode) {
- return;
- }
- }
-
- if let Some(s) = element_invalidation {
- debug!(" > Found element invalidation: {:?}", s);
- if self.insert_invalidation(s, InvalidationKind::Element, quirks_mode) {
- return;
- }
- }
-
- // The selector was of a form that we can't handle. Any element could
- // match it, so let's just bail out.
- debug!(" > Can't handle selector or OOMd, marking fully invalid");
- self.invalidate_fully()
- }
-
- fn insert_invalidation(
- &mut self,
- invalidation: Invalidation,
- kind: InvalidationKind,
- quirks_mode: QuirksMode,
- ) -> bool {
- match invalidation {
- Invalidation::Class(c) => {
- let entry = match self.classes.try_entry(c.0, quirks_mode) {
- Ok(e) => e,
- Err(..) => return false,
- };
- *entry.or_insert(InvalidationKind::None) |= kind;
- },
- Invalidation::ID(i) => {
- let entry = match self.ids.try_entry(i.0, quirks_mode) {
- Ok(e) => e,
- Err(..) => return false,
- };
- *entry.or_insert(InvalidationKind::None) |= kind;
- },
- Invalidation::LocalName { name, lower_name } => {
- let insert_lower = name != lower_name;
- if self.local_names.try_reserve(1).is_err() {
- return false;
- }
- let entry = self.local_names.entry(name);
- *entry.or_insert(InvalidationKind::None) |= kind;
- if insert_lower {
- if self.local_names.try_reserve(1).is_err() {
- return false;
- }
- let entry = self.local_names.entry(lower_name);
- *entry.or_insert(InvalidationKind::None) |= kind;
- }
- },
- }
-
- true
- }
-
- /// Collects invalidations for a given CSS rule, if not fully invalid
- /// already.
- ///
- /// TODO(emilio): we can't check whether the rule is inside a non-effective
- /// subtree, we potentially could do that.
- pub fn rule_changed<S>(
- &mut self,
- stylesheet: &S,
- rule: &CssRule,
- guard: &SharedRwLockReadGuard,
- device: &Device,
- quirks_mode: QuirksMode,
- change_kind: RuleChangeKind,
- ) where
- S: StylesheetInDocument,
- {
- use crate::stylesheets::CssRule::*;
-
- debug!("StylesheetInvalidationSet::rule_changed");
- if self.fully_invalid {
- return;
- }
-
- if !stylesheet.enabled() || !stylesheet.is_effective_for_device(device, guard) {
- debug!(" > Stylesheet was not effective");
- return; // Nothing to do here.
- }
-
- let is_generic_change = change_kind == RuleChangeKind::Generic;
-
- match *rule {
- Namespace(..) => {
- // It's not clear what handling changes for this correctly would
- // look like.
- },
- LayerStatement(..) => {
- // Layer statement insertions might alter styling order, so we need to always
- // invalidate fully.
- return self.invalidate_fully();
- },
- CounterStyle(..) |
- Page(..) |
- Property(..) |
- FontFeatureValues(..) |
- FontPaletteValues(..) |
- FontFace(..) |
- Keyframes(..) |
- Container(..) |
- Style(..) => {
- if is_generic_change {
- // TODO(emilio): We need to do this for selector / keyframe
- // name / font-face changes, because we don't have the old
- // selector / name. If we distinguish those changes
- // specially, then we can at least use this invalidation for
- // style declaration changes.
- return self.invalidate_fully();
- }
-
- self.collect_invalidations_for_rule(rule, guard, device, quirks_mode)
- },
- Document(..) | Import(..) | Media(..) | Supports(..) | LayerBlock(..) => {
- if !is_generic_change &&
- !EffectiveRules::is_effective(guard, device, quirks_mode, rule)
- {
- return;
- }
-
- let rules =
- EffectiveRulesIterator::effective_children(device, quirks_mode, guard, rule);
- for rule in rules {
- self.collect_invalidations_for_rule(rule, guard, device, quirks_mode);
- if self.fully_invalid {
- break;
- }
- }
- },
- }
- }
-
- /// Collects invalidations for a given CSS rule.
- fn collect_invalidations_for_rule(
- &mut self,
- rule: &CssRule,
- guard: &SharedRwLockReadGuard,
- device: &Device,
- quirks_mode: QuirksMode,
- ) {
- use crate::stylesheets::CssRule::*;
- debug!("StylesheetInvalidationSet::collect_invalidations_for_rule");
- debug_assert!(!self.fully_invalid, "Not worth to be here!");
-
- match *rule {
- Style(ref lock) => {
- let style_rule = lock.read_with(guard);
- for selector in &style_rule.selectors.0 {
- self.collect_invalidations(selector, quirks_mode);
- if self.fully_invalid {
- return;
- }
- }
- },
- Document(..) | Namespace(..) | Import(..) | Media(..) | Supports(..) |
- Container(..) | LayerStatement(..) | LayerBlock(..) => {
- // Do nothing, relevant nested rules are visited as part of the
- // iteration.
- },
- FontFace(..) => {
- // Do nothing, @font-face doesn't affect computed style
- // information. We'll restyle when the font face loads, if
- // needed.
- },
- Keyframes(ref lock) => {
- let keyframes_rule = lock.read_with(guard);
- if device.animation_name_may_be_referenced(&keyframes_rule.name) {
- debug!(
- " > Found @keyframes rule potentially referenced \
- from the page, marking the whole tree invalid."
- );
- self.fully_invalid = true;
- } else {
- // Do nothing, this animation can't affect the style of
- // existing elements.
- }
- },
- CounterStyle(..) |
- Page(..) |
- Property(..) |
- FontFeatureValues(..) |
- FontPaletteValues(..) => {
- debug!(" > Found unsupported rule, marking the whole subtree invalid.");
-
- // TODO(emilio): Can we do better here?
- //
- // At least in `@page`, we could check the relevant media, I
- // guess.
- self.fully_invalid = true;
- },
- }
- }
-}
diff --git a/components/style/invalidation/viewport_units.rs b/components/style/invalidation/viewport_units.rs
deleted file mode 100644
index 06faeb14c46..00000000000
--- a/components/style/invalidation/viewport_units.rs
+++ /dev/null
@@ -1,71 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
-
-//! Invalidates style of all elements that depend on viewport units.
-
-use crate::data::ViewportUnitUsage;
-use crate::dom::{TElement, TNode};
-use crate::invalidation::element::restyle_hints::RestyleHint;
-
-/// Invalidates style of all elements that depend on viewport units.
-///
-/// Returns whether any element was invalidated.
-pub fn invalidate<E>(root: E) -> bool
-where
- E: TElement,
-{
- debug!("invalidation::viewport_units::invalidate({:?})", root);
- invalidate_recursively(root)
-}
-
-fn invalidate_recursively<E>(element: E) -> bool
-where
- E: TElement,
-{
- let mut data = match element.mutate_data() {
- Some(data) => data,
- None => return false,
- };
-
- if data.hint.will_recascade_subtree() {
- debug!("invalidate_recursively: {:?} was already invalid", element);
- return false;
- }
-
- let usage = data.styles.viewport_unit_usage();
- let uses_viewport_units = usage != ViewportUnitUsage::None;
- if uses_viewport_units {
- debug!(
- "invalidate_recursively: {:?} uses viewport units {:?}",
- element, usage
- );
- }
-
- match usage {
- ViewportUnitUsage::None => {},
- ViewportUnitUsage::FromQuery => {
- data.hint.insert(RestyleHint::RESTYLE_SELF);
- },
- ViewportUnitUsage::FromDeclaration => {
- data.hint.insert(RestyleHint::RECASCADE_SELF);
- },
- }
-
- let mut any_children_invalid = false;
- for child in element.traversal_children() {
- if let Some(child) = child.as_element() {
- any_children_invalid |= invalidate_recursively(child);
- }
- }
-
- if any_children_invalid {
- debug!(
- "invalidate_recursively: Children of {:?} changed, setting dirty descendants",
- element
- );
- unsafe { element.set_dirty_descendants() }
- }
-
- uses_viewport_units || any_children_invalid
-}
diff --git a/components/style/lib.rs b/components/style/lib.rs
deleted file mode 100644
index 90a52e567b1..00000000000
--- a/components/style/lib.rs
+++ /dev/null
@@ -1,329 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
-
-//! Calculate [specified][specified] and [computed values][computed] from a
-//! tree of DOM nodes and a set of stylesheets.
-//!
-//! [computed]: https://drafts.csswg.org/css-cascade/#computed
-//! [specified]: https://drafts.csswg.org/css-cascade/#specified
-//!
-//! In particular, this crate contains the definitions of supported properties,
-//! the code to parse them into specified values and calculate the computed
-//! values based on the specified values, as well as the code to serialize both
-//! specified and computed values.
-//!
-//! The main entry point is [`recalc_style_at`][recalc_style_at].
-//!
-//! [recalc_style_at]: traversal/fn.recalc_style_at.html
-//!
-//! Major dependencies are the [cssparser][cssparser] and [selectors][selectors]
-//! crates.
-//!
-//! [cssparser]: ../cssparser/index.html
-//! [selectors]: ../selectors/index.html
-
-#![deny(missing_docs)]
-
-#[macro_use]
-extern crate bitflags;
-#[macro_use]
-extern crate cssparser;
-#[macro_use]
-extern crate debug_unreachable;
-#[macro_use]
-extern crate derive_more;
-#[macro_use]
-#[cfg(feature = "gecko")]
-extern crate gecko_profiler;
-#[cfg(feature = "gecko")]
-#[macro_use]
-pub mod gecko_string_cache;
-#[cfg(feature = "servo")]
-#[macro_use]
-extern crate html5ever;
-#[macro_use]
-extern crate lazy_static;
-#[macro_use]
-extern crate log;
-#[macro_use]
-extern crate malloc_size_of;
-#[macro_use]
-extern crate malloc_size_of_derive;
-#[cfg(feature = "gecko")]
-pub use nsstring;
-#[cfg(feature = "gecko")]
-extern crate num_cpus;
-#[macro_use]
-extern crate num_derive;
-#[macro_use]
-extern crate serde;
-pub use servo_arc;
-#[cfg(feature = "servo")]
-#[macro_use]
-extern crate servo_atoms;
-#[macro_use]
-extern crate static_assertions;
-#[macro_use]
-extern crate style_derive;
-#[macro_use]
-extern crate to_shmem_derive;
-
-#[macro_use]
-mod macros;
-
-pub mod animation;
-pub mod applicable_declarations;
-#[allow(missing_docs)] // TODO.
-#[cfg(feature = "servo")]
-pub mod attr;
-pub mod author_styles;
-pub mod bezier;
-pub mod bloom;
-pub mod color;
-#[path = "properties/computed_value_flags.rs"]
-pub mod computed_value_flags;
-pub mod context;
-pub mod counter_style;
-pub mod custom_properties;
-pub mod data;
-pub mod dom;
-pub mod dom_apis;
-pub mod driver;
-#[cfg(feature = "servo")]
-mod encoding_support;
-pub mod error_reporting;
-pub mod font_face;
-pub mod font_metrics;
-#[cfg(feature = "gecko")]
-#[allow(unsafe_code)]
-pub mod gecko_bindings;
-pub mod global_style_data;
-pub mod invalidation;
-#[allow(missing_docs)] // TODO.
-pub mod logical_geometry;
-pub mod matching;
-pub mod media_queries;
-pub mod parallel;
-pub mod parser;
-pub mod piecewise_linear;
-pub mod properties_and_values;
-#[macro_use]
-pub mod queries;
-pub mod rule_cache;
-pub mod rule_collector;
-pub mod rule_tree;
-pub mod scoped_tls;
-pub mod selector_map;
-pub mod selector_parser;
-pub mod shared_lock;
-pub mod sharing;
-pub mod str;
-pub mod style_adjuster;
-pub mod style_resolver;
-pub mod stylesheet_set;
-pub mod stylesheets;
-pub mod stylist;
-pub mod thread_state;
-pub mod traversal;
-pub mod traversal_flags;
-pub mod use_counters;
-#[macro_use]
-#[allow(non_camel_case_types)]
-pub mod values;
-
-#[cfg(feature = "gecko")]
-pub use crate::gecko_string_cache as string_cache;
-#[cfg(feature = "gecko")]
-pub use crate::gecko_string_cache::Atom;
-/// The namespace prefix type for Gecko, which is just an atom.
-#[cfg(feature = "gecko")]
-pub type Prefix = crate::values::AtomIdent;
-/// The local name of an element for Gecko, which is just an atom.
-#[cfg(feature = "gecko")]
-pub type LocalName = crate::values::AtomIdent;
-#[cfg(feature = "gecko")]
-pub use crate::gecko_string_cache::Namespace;
-
-#[cfg(feature = "servo")]
-pub use servo_atoms::Atom;
-
-#[cfg(feature = "servo")]
-#[allow(missing_docs)]
-pub type LocalName = crate::values::GenericAtomIdent<html5ever::LocalNameStaticSet>;
-#[cfg(feature = "servo")]
-#[allow(missing_docs)]
-pub type Namespace = crate::values::GenericAtomIdent<html5ever::NamespaceStaticSet>;
-#[cfg(feature = "servo")]
-#[allow(missing_docs)]
-pub type Prefix = crate::values::GenericAtomIdent<html5ever::PrefixStaticSet>;
-
-pub use style_traits::arc_slice::ArcSlice;
-pub use style_traits::owned_slice::OwnedSlice;
-pub use style_traits::owned_str::OwnedStr;
-
-use std::hash::{BuildHasher, Hash};
-
-#[macro_use]
-pub mod properties;
-
-#[cfg(feature = "gecko")]
-#[allow(unsafe_code)]
-pub mod gecko;
-
-// uses a macro from properties
-#[cfg(feature = "servo")]
-#[allow(unsafe_code)]
-pub mod servo;
-
-macro_rules! reexport_computed_values {
- ( $( { $name: ident } )+ ) => {
- /// Types for [computed values][computed].
- ///
- /// [computed]: https://drafts.csswg.org/css-cascade/#computed
- pub mod computed_values {
- $(
- pub use crate::properties::longhands::$name::computed_value as $name;
- )+
- // Don't use a side-specific name needlessly:
- pub use crate::properties::longhands::border_top_style::computed_value as border_style;
- }
- }
-}
-longhand_properties_idents!(reexport_computed_values);
-#[cfg(feature = "gecko")]
-use crate::gecko_string_cache::WeakAtom;
-#[cfg(feature = "servo")]
-use servo_atoms::Atom as WeakAtom;
-
-/// Extension methods for selectors::attr::CaseSensitivity
-pub trait CaseSensitivityExt {
- /// Return whether two atoms compare equal according to this case sensitivity.
- fn eq_atom(self, a: &WeakAtom, b: &WeakAtom) -> bool;
-}
-
-impl CaseSensitivityExt for selectors::attr::CaseSensitivity {
- fn eq_atom(self, a: &WeakAtom, b: &WeakAtom) -> bool {
- match self {
- selectors::attr::CaseSensitivity::CaseSensitive => a == b,
- selectors::attr::CaseSensitivity::AsciiCaseInsensitive => a.eq_ignore_ascii_case(b),
- }
- }
-}
-
-/// A trait pretty much similar to num_traits::Zero, but without the need of
-/// implementing `Add`.
-pub trait Zero {
- /// Returns the zero value.
- fn zero() -> Self;
-
- /// Returns whether this value is zero.
- fn is_zero(&self) -> bool;
-}
-
-impl<T> Zero for T
-where
- T: num_traits::Zero,
-{
- fn zero() -> Self {
- <Self as num_traits::Zero>::zero()
- }
-
- fn is_zero(&self) -> bool {
- <Self as num_traits::Zero>::is_zero(self)
- }
-}
-
-/// A trait implementing a function to tell if the number is zero without a percent
-pub trait ZeroNoPercent {
- /// So, `0px` should return `true`, but `0%` or `1px` should return `false`
- fn is_zero_no_percent(&self) -> bool;
-}
-
-/// A trait pretty much similar to num_traits::One, but without the need of
-/// implementing `Mul`.
-pub trait One {
- /// Reutrns the one value.
- fn one() -> Self;
-
- /// Returns whether this value is one.
- fn is_one(&self) -> bool;
-}
-
-impl<T> One for T
-where
- T: num_traits::One + PartialEq,
-{
- fn one() -> Self {
- <Self as num_traits::One>::one()
- }
-
- fn is_one(&self) -> bool {
- *self == One::one()
- }
-}
-
-/// An allocation error.
-///
-/// TODO(emilio): Would be nice to have more information here, or for SmallVec
-/// to return the standard error type (and then we can just return that).
-///
-/// But given we use these mostly to bail out and ignore them, it's not a big
-/// deal.
-#[derive(Debug)]
-pub struct AllocErr;
-
-impl From<smallvec::CollectionAllocErr> for AllocErr {
- #[inline]
- fn from(_: smallvec::CollectionAllocErr) -> Self {
- Self
- }
-}
-
-impl From<std::collections::TryReserveError> for AllocErr {
- #[inline]
- fn from(_: std::collections::TryReserveError) -> Self {
- Self
- }
-}
-
-/// Shrink the capacity of the collection if needed.
-pub(crate) trait ShrinkIfNeeded {
- fn shrink_if_needed(&mut self);
-}
-
-/// We shrink the capacity of a collection if we're wasting more than a 25% of
-/// its capacity, and if the collection is arbitrarily big enough
-/// (>= CAPACITY_THRESHOLD entries).
-#[inline]
-fn should_shrink(len: usize, capacity: usize) -> bool {
- const CAPACITY_THRESHOLD: usize = 64;
- capacity >= CAPACITY_THRESHOLD && len + capacity / 4 < capacity
-}
-
-impl<K, V, H> ShrinkIfNeeded for std::collections::HashMap<K, V, H>
-where
- K: Eq + Hash,
- H: BuildHasher,
-{
- fn shrink_if_needed(&mut self) {
- if should_shrink(self.len(), self.capacity()) {
- self.shrink_to_fit();
- }
- }
-}
-
-impl<T, H> ShrinkIfNeeded for std::collections::HashSet<T, H>
-where
- T: Eq + Hash,
- H: BuildHasher,
-{
- fn shrink_if_needed(&mut self) {
- if should_shrink(self.len(), self.capacity()) {
- self.shrink_to_fit();
- }
- }
-}
-
-// TODO(emilio): Measure and see if we're wasting a lot of memory on Vec /
-// SmallVec, and if so consider shrinking those as well.
diff --git a/components/style/logical_geometry.rs b/components/style/logical_geometry.rs
deleted file mode 100644
index 9a823a5a24a..00000000000
--- a/components/style/logical_geometry.rs
+++ /dev/null
@@ -1,1522 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
-
-//! Geometry in flow-relative space.
-
-use crate::properties::style_structs;
-use euclid::default::{Point2D, Rect, SideOffsets2D, Size2D};
-use euclid::num::Zero;
-use std::cmp::{max, min};
-use std::fmt::{self, Debug, Error, Formatter};
-use std::ops::{Add, Sub};
-use unicode_bidi as bidi;
-
-pub enum BlockFlowDirection {
- TopToBottom,
- RightToLeft,
- LeftToRight,
-}
-
-pub enum InlineBaseDirection {
- LeftToRight,
- RightToLeft,
-}
-
-// TODO: improve the readability of the WritingMode serialization, refer to the Debug:fmt()
-bitflags!(
- #[cfg_attr(feature = "servo", derive(MallocSizeOf, Serialize))]
- #[repr(C)]
- pub struct WritingMode: u8 {
- /// A vertical writing mode; writing-mode is vertical-rl,
- /// vertical-lr, sideways-lr, or sideways-rl.
- const VERTICAL = 1 << 0;
- /// The inline flow direction is reversed against the physical
- /// direction (i.e. right-to-left or bottom-to-top); writing-mode is
- /// sideways-lr or direction is rtl (but not both).
- ///
- /// (This bit can be derived from the others, but we store it for
- /// convenience.)
- const INLINE_REVERSED = 1 << 1;
- /// A vertical writing mode whose block progression direction is left-
- /// to-right; writing-mode is vertical-lr or sideways-lr.
- ///
- /// Never set without VERTICAL.
- const VERTICAL_LR = 1 << 2;
- /// The line-over/line-under sides are inverted with respect to the
- /// block-start/block-end edge; writing-mode is vertical-lr.
- ///
- /// Never set without VERTICAL and VERTICAL_LR.
- const LINE_INVERTED = 1 << 3;
- /// direction is rtl.
- const RTL = 1 << 4;
- /// All text within a vertical writing mode is displayed sideways
- /// and runs top-to-bottom or bottom-to-top; set in these cases:
- ///
- /// * writing-mode: sideways-rl;
- /// * writing-mode: sideways-lr;
- ///
- /// Never set without VERTICAL.
- const VERTICAL_SIDEWAYS = 1 << 5;
- /// Similar to VERTICAL_SIDEWAYS, but is set via text-orientation;
- /// set in these cases:
- ///
- /// * writing-mode: vertical-rl; text-orientation: sideways;
- /// * writing-mode: vertical-lr; text-orientation: sideways;
- ///
- /// Never set without VERTICAL.
- const TEXT_SIDEWAYS = 1 << 6;
- /// Horizontal text within a vertical writing mode is displayed with each
- /// glyph upright; set in these cases:
- ///
- /// * writing-mode: vertical-rl; text-orientation: upright;
- /// * writing-mode: vertical-lr: text-orientation: upright;
- ///
- /// Never set without VERTICAL.
- const UPRIGHT = 1 << 7;
- }
-);
-
-impl WritingMode {
- /// Return a WritingMode bitflags from the relevant CSS properties.
- pub fn new(inheritedbox_style: &style_structs::InheritedBox) -> Self {
- use crate::properties::longhands::direction::computed_value::T as Direction;
- use crate::properties::longhands::writing_mode::computed_value::T as SpecifiedWritingMode;
-
- let mut flags = WritingMode::empty();
-
- let direction = inheritedbox_style.clone_direction();
- let writing_mode = inheritedbox_style.clone_writing_mode();
-
- match direction {
- Direction::Ltr => {},
- Direction::Rtl => {
- flags.insert(WritingMode::RTL);
- },
- }
-
- match writing_mode {
- SpecifiedWritingMode::HorizontalTb => {
- if direction == Direction::Rtl {
- flags.insert(WritingMode::INLINE_REVERSED);
- }
- },
- SpecifiedWritingMode::VerticalRl => {
- flags.insert(WritingMode::VERTICAL);
- if direction == Direction::Rtl {
- flags.insert(WritingMode::INLINE_REVERSED);
- }
- },
- SpecifiedWritingMode::VerticalLr => {
- flags.insert(WritingMode::VERTICAL);
- flags.insert(WritingMode::VERTICAL_LR);
- flags.insert(WritingMode::LINE_INVERTED);
- if direction == Direction::Rtl {
- flags.insert(WritingMode::INLINE_REVERSED);
- }
- },
- #[cfg(feature = "gecko")]
- SpecifiedWritingMode::SidewaysRl => {
- flags.insert(WritingMode::VERTICAL);
- flags.insert(WritingMode::VERTICAL_SIDEWAYS);
- if direction == Direction::Rtl {
- flags.insert(WritingMode::INLINE_REVERSED);
- }
- },
- #[cfg(feature = "gecko")]
- SpecifiedWritingMode::SidewaysLr => {
- flags.insert(WritingMode::VERTICAL);
- flags.insert(WritingMode::VERTICAL_LR);
- flags.insert(WritingMode::VERTICAL_SIDEWAYS);
- if direction == Direction::Ltr {
- flags.insert(WritingMode::INLINE_REVERSED);
- }
- },
- }
-
- #[cfg(feature = "gecko")]
- {
- use crate::properties::longhands::text_orientation::computed_value::T as TextOrientation;
-
- // text-orientation only has an effect for vertical-rl and
- // vertical-lr values of writing-mode.
- match writing_mode {
- SpecifiedWritingMode::VerticalRl | SpecifiedWritingMode::VerticalLr => {
- match inheritedbox_style.clone_text_orientation() {
- TextOrientation::Mixed => {},
- TextOrientation::Upright => {
- flags.insert(WritingMode::UPRIGHT);
-
- // https://drafts.csswg.org/css-writing-modes-3/#valdef-text-orientation-upright:
- //
- // > This value causes the used value of direction
- // > to be ltr, and for the purposes of bidi
- // > reordering, causes all characters to be treated
- // > as strong LTR.
- flags.remove(WritingMode::RTL);
- flags.remove(WritingMode::INLINE_REVERSED);
- },
- TextOrientation::Sideways => {
- flags.insert(WritingMode::TEXT_SIDEWAYS);
- },
- }
- },
- _ => {},
- }
- }
-
- flags
- }
-
- /// Returns the `horizontal-tb` value.
- pub fn horizontal_tb() -> Self {
- Self::from_bits_truncate(0)
- }
-
- #[inline]
- pub fn is_vertical(&self) -> bool {
- self.intersects(WritingMode::VERTICAL)
- }
-
- #[inline]
- pub fn is_horizontal(&self) -> bool {
- !self.is_vertical()
- }
-
- /// Assuming .is_vertical(), does the block direction go left to right?
- #[inline]
- pub fn is_vertical_lr(&self) -> bool {
- self.intersects(WritingMode::VERTICAL_LR)
- }
-
- /// Assuming .is_vertical(), does the inline direction go top to bottom?
- #[inline]
- pub fn is_inline_tb(&self) -> bool {
- // https://drafts.csswg.org/css-writing-modes-3/#logical-to-physical
- !self.intersects(WritingMode::INLINE_REVERSED)
- }
-
- #[inline]
- pub fn is_bidi_ltr(&self) -> bool {
- !self.intersects(WritingMode::RTL)
- }
-
- #[inline]
- pub fn is_sideways(&self) -> bool {
- self.intersects(WritingMode::VERTICAL_SIDEWAYS | WritingMode::TEXT_SIDEWAYS)
- }
-
- #[inline]
- pub fn is_upright(&self) -> bool {
- self.intersects(WritingMode::UPRIGHT)
- }
-
- /// https://drafts.csswg.org/css-writing-modes/#logical-to-physical
- ///
- /// | Return | line-left is… | line-right is… |
- /// |---------|---------------|----------------|
- /// | `true` | inline-start | inline-end |
- /// | `false` | inline-end | inline-start |
- #[inline]
- pub fn line_left_is_inline_start(&self) -> bool {
- // https://drafts.csswg.org/css-writing-modes/#inline-start
- // “For boxes with a used direction value of ltr, this means the line-left side.
- // For boxes with a used direction value of rtl, this means the line-right side.”
- self.is_bidi_ltr()
- }
-
- #[inline]
- pub fn inline_start_physical_side(&self) -> PhysicalSide {
- match (self.is_vertical(), self.is_inline_tb(), self.is_bidi_ltr()) {
- (false, _, true) => PhysicalSide::Left,
- (false, _, false) => PhysicalSide::Right,
- (true, true, _) => PhysicalSide::Top,
- (true, false, _) => PhysicalSide::Bottom,
- }
- }
-
- #[inline]
- pub fn inline_end_physical_side(&self) -> PhysicalSide {
- match (self.is_vertical(), self.is_inline_tb(), self.is_bidi_ltr()) {
- (false, _, true) => PhysicalSide::Right,
- (false, _, false) => PhysicalSide::Left,
- (true, true, _) => PhysicalSide::Bottom,
- (true, false, _) => PhysicalSide::Top,
- }
- }
-
- #[inline]
- pub fn block_start_physical_side(&self) -> PhysicalSide {
- match (self.is_vertical(), self.is_vertical_lr()) {
- (false, _) => PhysicalSide::Top,
- (true, true) => PhysicalSide::Left,
- (true, false) => PhysicalSide::Right,
- }
- }
-
- #[inline]
- pub fn block_end_physical_side(&self) -> PhysicalSide {
- match (self.is_vertical(), self.is_vertical_lr()) {
- (false, _) => PhysicalSide::Bottom,
- (true, true) => PhysicalSide::Right,
- (true, false) => PhysicalSide::Left,
- }
- }
-
- #[inline]
- fn physical_sides_to_corner(
- block_side: PhysicalSide,
- inline_side: PhysicalSide,
- ) -> PhysicalCorner {
- match (block_side, inline_side) {
- (PhysicalSide::Top, PhysicalSide::Left) | (PhysicalSide::Left, PhysicalSide::Top) => {
- PhysicalCorner::TopLeft
- },
- (PhysicalSide::Top, PhysicalSide::Right) | (PhysicalSide::Right, PhysicalSide::Top) => {
- PhysicalCorner::TopRight
- },
- (PhysicalSide::Bottom, PhysicalSide::Right) |
- (PhysicalSide::Right, PhysicalSide::Bottom) => PhysicalCorner::BottomRight,
- (PhysicalSide::Bottom, PhysicalSide::Left) |
- (PhysicalSide::Left, PhysicalSide::Bottom) => PhysicalCorner::BottomLeft,
- _ => unreachable!("block and inline sides must be orthogonal"),
- }
- }
-
- #[inline]
- pub fn start_start_physical_corner(&self) -> PhysicalCorner {
- WritingMode::physical_sides_to_corner(
- self.block_start_physical_side(),
- self.inline_start_physical_side(),
- )
- }
-
- #[inline]
- pub fn start_end_physical_corner(&self) -> PhysicalCorner {
- WritingMode::physical_sides_to_corner(
- self.block_start_physical_side(),
- self.inline_end_physical_side(),
- )
- }
-
- #[inline]
- pub fn end_start_physical_corner(&self) -> PhysicalCorner {
- WritingMode::physical_sides_to_corner(
- self.block_end_physical_side(),
- self.inline_start_physical_side(),
- )
- }
-
- #[inline]
- pub fn end_end_physical_corner(&self) -> PhysicalCorner {
- WritingMode::physical_sides_to_corner(
- self.block_end_physical_side(),
- self.inline_end_physical_side(),
- )
- }
-
- #[inline]
- pub fn block_flow_direction(&self) -> BlockFlowDirection {
- match (self.is_vertical(), self.is_vertical_lr()) {
- (false, _) => BlockFlowDirection::TopToBottom,
- (true, true) => BlockFlowDirection::LeftToRight,
- (true, false) => BlockFlowDirection::RightToLeft,
- }
- }
-
- #[inline]
- pub fn inline_base_direction(&self) -> InlineBaseDirection {
- if self.intersects(WritingMode::RTL) {
- InlineBaseDirection::RightToLeft
- } else {
- InlineBaseDirection::LeftToRight
- }
- }
-
- #[inline]
- /// The default bidirectional embedding level for this writing mode.
- ///
- /// Returns bidi level 0 if the mode is LTR, or 1 otherwise.
- pub fn to_bidi_level(&self) -> bidi::Level {
- if self.is_bidi_ltr() {
- bidi::Level::ltr()
- } else {
- bidi::Level::rtl()
- }
- }
-}
-
-impl fmt::Display for WritingMode {
- fn fmt(&self, formatter: &mut Formatter) -> Result<(), Error> {
- if self.is_vertical() {
- write!(formatter, "V")?;
- if self.is_vertical_lr() {
- write!(formatter, " LR")?;
- } else {
- write!(formatter, " RL")?;
- }
- if self.is_sideways() {
- write!(formatter, " Sideways")?;
- }
- if self.intersects(WritingMode::LINE_INVERTED) {
- write!(formatter, " Inverted")?;
- }
- } else {
- write!(formatter, "H")?;
- }
- if self.is_bidi_ltr() {
- write!(formatter, " LTR")
- } else {
- write!(formatter, " RTL")
- }
- }
-}
-
-/// Wherever logical geometry is used, the writing mode is known based on context:
-/// every method takes a `mode` parameter.
-/// However, this context is easy to get wrong.
-/// In debug builds only, logical geometry objects store their writing mode
-/// (in addition to taking it as a parameter to methods) and check it.
-/// In non-debug builds, make this storage zero-size and the checks no-ops.
-#[cfg(not(debug_assertions))]
-#[derive(Clone, Copy, Eq, PartialEq)]
-#[cfg_attr(feature = "servo", derive(Serialize))]
-struct DebugWritingMode;
-
-#[cfg(debug_assertions)]
-#[derive(Clone, Copy, Eq, PartialEq)]
-#[cfg_attr(feature = "servo", derive(Serialize))]
-struct DebugWritingMode {
- mode: WritingMode,
-}
-
-#[cfg(not(debug_assertions))]
-impl DebugWritingMode {
- #[inline]
- fn check(&self, _other: WritingMode) {}
-
- #[inline]
- fn check_debug(&self, _other: DebugWritingMode) {}
-
- #[inline]
- fn new(_mode: WritingMode) -> DebugWritingMode {
- DebugWritingMode
- }
-}
-
-#[cfg(debug_assertions)]
-impl DebugWritingMode {
- #[inline]
- fn check(&self, other: WritingMode) {
- assert_eq!(self.mode, other)
- }
-
- #[inline]
- fn check_debug(&self, other: DebugWritingMode) {
- assert_eq!(self.mode, other.mode)
- }
-
- #[inline]
- fn new(mode: WritingMode) -> DebugWritingMode {
- DebugWritingMode { mode: mode }
- }
-}
-
-impl Debug for DebugWritingMode {
- #[cfg(not(debug_assertions))]
- fn fmt(&self, formatter: &mut Formatter) -> Result<(), Error> {
- write!(formatter, "?")
- }
-
- #[cfg(debug_assertions)]
- fn fmt(&self, formatter: &mut Formatter) -> Result<(), Error> {
- write!(formatter, "{}", self.mode)
- }
-}
-
-// Used to specify the logical direction.
-#[derive(Clone, Copy, Debug, PartialEq)]
-#[cfg_attr(feature = "servo", derive(Serialize))]
-pub enum Direction {
- Inline,
- Block,
-}
-
-/// A 2D size in flow-relative dimensions
-#[derive(Clone, Copy, Eq, PartialEq)]
-#[cfg_attr(feature = "servo", derive(Serialize))]
-pub struct LogicalSize<T> {
- pub inline: T, // inline-size, a.k.a. logical width, a.k.a. measure
- pub block: T, // block-size, a.k.a. logical height, a.k.a. extent
- debug_writing_mode: DebugWritingMode,
-}
-
-impl<T: Debug> Debug for LogicalSize<T> {
- fn fmt(&self, formatter: &mut Formatter) -> Result<(), Error> {
- write!(
- formatter,
- "LogicalSize({:?}, i{:?}×b{:?})",
- self.debug_writing_mode, self.inline, self.block
- )
- }
-}
-
-// Can not implement the Zero trait: its zero() method does not have the `mode` parameter.
-impl<T: Zero> LogicalSize<T> {
- #[inline]
- pub fn zero(mode: WritingMode) -> LogicalSize<T> {
- LogicalSize {
- inline: Zero::zero(),
- block: Zero::zero(),
- debug_writing_mode: DebugWritingMode::new(mode),
- }
- }
-}
-
-impl<T> LogicalSize<T> {
- #[inline]
- pub fn new(mode: WritingMode, inline: T, block: T) -> LogicalSize<T> {
- LogicalSize {
- inline: inline,
- block: block,
- debug_writing_mode: DebugWritingMode::new(mode),
- }
- }
-
- #[inline]
- pub fn from_physical(mode: WritingMode, size: Size2D<T>) -> LogicalSize<T> {
- if mode.is_vertical() {
- LogicalSize::new(mode, size.height, size.width)
- } else {
- LogicalSize::new(mode, size.width, size.height)
- }
- }
-}
-
-impl<T: Copy> LogicalSize<T> {
- #[inline]
- pub fn width(&self, mode: WritingMode) -> T {
- self.debug_writing_mode.check(mode);
- if mode.is_vertical() {
- self.block
- } else {
- self.inline
- }
- }
-
- #[inline]
- pub fn set_width(&mut self, mode: WritingMode, width: T) {
- self.debug_writing_mode.check(mode);
- if mode.is_vertical() {
- self.block = width
- } else {
- self.inline = width
- }
- }
-
- #[inline]
- pub fn height(&self, mode: WritingMode) -> T {
- self.debug_writing_mode.check(mode);
- if mode.is_vertical() {
- self.inline
- } else {
- self.block
- }
- }
-
- #[inline]
- pub fn set_height(&mut self, mode: WritingMode, height: T) {
- self.debug_writing_mode.check(mode);
- if mode.is_vertical() {
- self.inline = height
- } else {
- self.block = height
- }
- }
-
- #[inline]
- pub fn to_physical(&self, mode: WritingMode) -> Size2D<T> {
- self.debug_writing_mode.check(mode);
- if mode.is_vertical() {
- Size2D::new(self.block, self.inline)
- } else {
- Size2D::new(self.inline, self.block)
- }
- }
-
- #[inline]
- pub fn convert(&self, mode_from: WritingMode, mode_to: WritingMode) -> LogicalSize<T> {
- if mode_from == mode_to {
- self.debug_writing_mode.check(mode_from);
- *self
- } else {
- LogicalSize::from_physical(mode_to, self.to_physical(mode_from))
- }
- }
-}
-
-impl<T: Add<T, Output = T>> Add for LogicalSize<T> {
- type Output = LogicalSize<T>;
-
- #[inline]
- fn add(self, other: LogicalSize<T>) -> LogicalSize<T> {
- self.debug_writing_mode
- .check_debug(other.debug_writing_mode);
- LogicalSize {
- debug_writing_mode: self.debug_writing_mode,
- inline: self.inline + other.inline,
- block: self.block + other.block,
- }
- }
-}
-
-// TODO(servo#30577) remove this once underlying bugs are fixed
-impl<T: Add<T, Output = T>> LogicalSize<T> {
- #[inline]
- pub fn add_or_warn(self, other: LogicalSize<T>) -> LogicalSize<T> {
- #[cfg(debug_assertions)]
- if !(self.debug_writing_mode.mode == other.debug_writing_mode.mode) {
- log::warn!("debug assertion failed! self.debug_writing_mode.mode == other.debug_writing_mode.mode");
- }
- LogicalSize {
- debug_writing_mode: self.debug_writing_mode,
- inline: self.inline + other.inline,
- block: self.block + other.block,
- }
- }
-}
-
-impl<T: Sub<T, Output = T>> Sub for LogicalSize<T> {
- type Output = LogicalSize<T>;
-
- #[inline]
- fn sub(self, other: LogicalSize<T>) -> LogicalSize<T> {
- self.debug_writing_mode
- .check_debug(other.debug_writing_mode);
- LogicalSize {
- debug_writing_mode: self.debug_writing_mode,
- inline: self.inline - other.inline,
- block: self.block - other.block,
- }
- }
-}
-
-/// A 2D point in flow-relative dimensions
-#[derive(Clone, Copy, Eq, PartialEq)]
-#[cfg_attr(feature = "servo", derive(Serialize))]
-pub struct LogicalPoint<T> {
- /// inline-axis coordinate
- pub i: T,
- /// block-axis coordinate
- pub b: T,
- debug_writing_mode: DebugWritingMode,
-}
-
-impl<T: Debug> Debug for LogicalPoint<T> {
- fn fmt(&self, formatter: &mut Formatter) -> Result<(), Error> {
- write!(
- formatter,
- "LogicalPoint({:?} (i{:?}, b{:?}))",
- self.debug_writing_mode, self.i, self.b
- )
- }
-}
-
-// Can not implement the Zero trait: its zero() method does not have the `mode` parameter.
-impl<T: Zero> LogicalPoint<T> {
- #[inline]
- pub fn zero(mode: WritingMode) -> LogicalPoint<T> {
- LogicalPoint {
- i: Zero::zero(),
- b: Zero::zero(),
- debug_writing_mode: DebugWritingMode::new(mode),
- }
- }
-}
-
-impl<T: Copy> LogicalPoint<T> {
- #[inline]
- pub fn new(mode: WritingMode, i: T, b: T) -> LogicalPoint<T> {
- LogicalPoint {
- i: i,
- b: b,
- debug_writing_mode: DebugWritingMode::new(mode),
- }
- }
-}
-
-impl<T: Copy + Sub<T, Output = T>> LogicalPoint<T> {
- #[inline]
- pub fn from_physical(
- mode: WritingMode,
- point: Point2D<T>,
- container_size: Size2D<T>,
- ) -> LogicalPoint<T> {
- if mode.is_vertical() {
- LogicalPoint {
- i: if mode.is_inline_tb() {
- point.y
- } else {
- container_size.height - point.y
- },
- b: if mode.is_vertical_lr() {
- point.x
- } else {
- container_size.width - point.x
- },
- debug_writing_mode: DebugWritingMode::new(mode),
- }
- } else {
- LogicalPoint {
- i: if mode.is_bidi_ltr() {
- point.x
- } else {
- container_size.width - point.x
- },
- b: point.y,
- debug_writing_mode: DebugWritingMode::new(mode),
- }
- }
- }
-
- #[inline]
- pub fn x(&self, mode: WritingMode, container_size: Size2D<T>) -> T {
- self.debug_writing_mode.check(mode);
- if mode.is_vertical() {
- if mode.is_vertical_lr() {
- self.b
- } else {
- container_size.width - self.b
- }
- } else {
- if mode.is_bidi_ltr() {
- self.i
- } else {
- container_size.width - self.i
- }
- }
- }
-
- #[inline]
- pub fn set_x(&mut self, mode: WritingMode, x: T, container_size: Size2D<T>) {
- self.debug_writing_mode.check(mode);
- if mode.is_vertical() {
- self.b = if mode.is_vertical_lr() {
- x
- } else {
- container_size.width - x
- }
- } else {
- self.i = if mode.is_bidi_ltr() {
- x
- } else {
- container_size.width - x
- }
- }
- }
-
- #[inline]
- pub fn y(&self, mode: WritingMode, container_size: Size2D<T>) -> T {
- self.debug_writing_mode.check(mode);
- if mode.is_vertical() {
- if mode.is_inline_tb() {
- self.i
- } else {
- container_size.height - self.i
- }
- } else {
- self.b
- }
- }
-
- #[inline]
- pub fn set_y(&mut self, mode: WritingMode, y: T, container_size: Size2D<T>) {
- self.debug_writing_mode.check(mode);
- if mode.is_vertical() {
- self.i = if mode.is_inline_tb() {
- y
- } else {
- container_size.height - y
- }
- } else {
- self.b = y
- }
- }
-
- #[inline]
- pub fn to_physical(&self, mode: WritingMode, container_size: Size2D<T>) -> Point2D<T> {
- self.debug_writing_mode.check(mode);
- if mode.is_vertical() {
- Point2D::new(
- if mode.is_vertical_lr() {
- self.b
- } else {
- container_size.width - self.b
- },
- if mode.is_inline_tb() {
- self.i
- } else {
- container_size.height - self.i
- },
- )
- } else {
- Point2D::new(
- if mode.is_bidi_ltr() {
- self.i
- } else {
- container_size.width - self.i
- },
- self.b,
- )
- }
- }
-
- // TODO(servo#30577) remove this once underlying bugs are fixed
- #[inline]
- pub fn to_physical_or_warn(&self, mode: WritingMode, container_size: Size2D<T>) -> Point2D<T> {
- #[cfg(debug_assertions)]
- if !(self.debug_writing_mode.mode == mode) {
- log::warn!("debug assertion failed! self.debug_writing_mode.mode == mode");
- }
- if mode.is_vertical() {
- Point2D::new(
- if mode.is_vertical_lr() {
- self.b
- } else {
- container_size.width - self.b
- },
- if mode.is_inline_tb() {
- self.i
- } else {
- container_size.height - self.i
- },
- )
- } else {
- Point2D::new(
- if mode.is_bidi_ltr() {
- self.i
- } else {
- container_size.width - self.i
- },
- self.b,
- )
- }
- }
-
- #[inline]
- pub fn convert(
- &self,
- mode_from: WritingMode,
- mode_to: WritingMode,
- container_size: Size2D<T>,
- ) -> LogicalPoint<T> {
- if mode_from == mode_to {
- self.debug_writing_mode.check(mode_from);
- *self
- } else {
- LogicalPoint::from_physical(
- mode_to,
- self.to_physical(mode_from, container_size),
- container_size,
- )
- }
- }
-}
-
-impl<T: Copy + Add<T, Output = T>> LogicalPoint<T> {
- /// This doesn’t really makes sense,
- /// but happens when dealing with multiple origins.
- #[inline]
- pub fn add_point(&self, other: &LogicalPoint<T>) -> LogicalPoint<T> {
- self.debug_writing_mode
- .check_debug(other.debug_writing_mode);
- LogicalPoint {
- debug_writing_mode: self.debug_writing_mode,
- i: self.i + other.i,
- b: self.b + other.b,
- }
- }
-}
-
-impl<T: Copy + Add<T, Output = T>> Add<LogicalSize<T>> for LogicalPoint<T> {
- type Output = LogicalPoint<T>;
-
- #[inline]
- fn add(self, other: LogicalSize<T>) -> LogicalPoint<T> {
- self.debug_writing_mode
- .check_debug(other.debug_writing_mode);
- LogicalPoint {
- debug_writing_mode: self.debug_writing_mode,
- i: self.i + other.inline,
- b: self.b + other.block,
- }
- }
-}
-
-impl<T: Copy + Sub<T, Output = T>> Sub<LogicalSize<T>> for LogicalPoint<T> {
- type Output = LogicalPoint<T>;
-
- #[inline]
- fn sub(self, other: LogicalSize<T>) -> LogicalPoint<T> {
- self.debug_writing_mode
- .check_debug(other.debug_writing_mode);
- LogicalPoint {
- debug_writing_mode: self.debug_writing_mode,
- i: self.i - other.inline,
- b: self.b - other.block,
- }
- }
-}
-
-/// A "margin" in flow-relative dimensions
-/// Represents the four sides of the margins, borders, or padding of a CSS box,
-/// or a combination of those.
-/// A positive "margin" can be added to a rectangle to obtain a bigger rectangle.
-#[derive(Clone, Copy, Eq, PartialEq)]
-#[cfg_attr(feature = "servo", derive(Serialize))]
-pub struct LogicalMargin<T> {
- pub block_start: T,
- pub inline_end: T,
- pub block_end: T,
- pub inline_start: T,
- debug_writing_mode: DebugWritingMode,
-}
-
-impl<T: Debug> Debug for LogicalMargin<T> {
- fn fmt(&self, formatter: &mut Formatter) -> Result<(), Error> {
- let writing_mode_string = if cfg!(debug_assertions) {
- format!("{:?}, ", self.debug_writing_mode)
- } else {
- "".to_owned()
- };
-
- write!(
- formatter,
- "LogicalMargin({}i:{:?}..{:?} b:{:?}..{:?})",
- writing_mode_string,
- self.inline_start,
- self.inline_end,
- self.block_start,
- self.block_end
- )
- }
-}
-
-impl<T: Zero> LogicalMargin<T> {
- #[inline]
- pub fn zero(mode: WritingMode) -> LogicalMargin<T> {
- LogicalMargin {
- block_start: Zero::zero(),
- inline_end: Zero::zero(),
- block_end: Zero::zero(),
- inline_start: Zero::zero(),
- debug_writing_mode: DebugWritingMode::new(mode),
- }
- }
-}
-
-impl<T> LogicalMargin<T> {
- #[inline]
- pub fn new(
- mode: WritingMode,
- block_start: T,
- inline_end: T,
- block_end: T,
- inline_start: T,
- ) -> LogicalMargin<T> {
- LogicalMargin {
- block_start,
- inline_end,
- block_end,
- inline_start,
- debug_writing_mode: DebugWritingMode::new(mode),
- }
- }
-
- #[inline]
- pub fn from_physical(mode: WritingMode, offsets: SideOffsets2D<T>) -> LogicalMargin<T> {
- let block_start;
- let inline_end;
- let block_end;
- let inline_start;
- if mode.is_vertical() {
- if mode.is_vertical_lr() {
- block_start = offsets.left;
- block_end = offsets.right;
- } else {
- block_start = offsets.right;
- block_end = offsets.left;
- }
- if mode.is_inline_tb() {
- inline_start = offsets.top;
- inline_end = offsets.bottom;
- } else {
- inline_start = offsets.bottom;
- inline_end = offsets.top;
- }
- } else {
- block_start = offsets.top;
- block_end = offsets.bottom;
- if mode.is_bidi_ltr() {
- inline_start = offsets.left;
- inline_end = offsets.right;
- } else {
- inline_start = offsets.right;
- inline_end = offsets.left;
- }
- }
- LogicalMargin::new(mode, block_start, inline_end, block_end, inline_start)
- }
-}
-
-impl<T: Copy> LogicalMargin<T> {
- #[inline]
- pub fn new_all_same(mode: WritingMode, value: T) -> LogicalMargin<T> {
- LogicalMargin::new(mode, value, value, value, value)
- }
-
- #[inline]
- pub fn top(&self, mode: WritingMode) -> T {
- self.debug_writing_mode.check(mode);
- if mode.is_vertical() {
- if mode.is_inline_tb() {
- self.inline_start
- } else {
- self.inline_end
- }
- } else {
- self.block_start
- }
- }
-
- #[inline]
- pub fn set_top(&mut self, mode: WritingMode, top: T) {
- self.debug_writing_mode.check(mode);
- if mode.is_vertical() {
- if mode.is_inline_tb() {
- self.inline_start = top
- } else {
- self.inline_end = top
- }
- } else {
- self.block_start = top
- }
- }
-
- #[inline]
- pub fn right(&self, mode: WritingMode) -> T {
- self.debug_writing_mode.check(mode);
- if mode.is_vertical() {
- if mode.is_vertical_lr() {
- self.block_end
- } else {
- self.block_start
- }
- } else {
- if mode.is_bidi_ltr() {
- self.inline_end
- } else {
- self.inline_start
- }
- }
- }
-
- #[inline]
- pub fn set_right(&mut self, mode: WritingMode, right: T) {
- self.debug_writing_mode.check(mode);
- if mode.is_vertical() {
- if mode.is_vertical_lr() {
- self.block_end = right
- } else {
- self.block_start = right
- }
- } else {
- if mode.is_bidi_ltr() {
- self.inline_end = right
- } else {
- self.inline_start = right
- }
- }
- }
-
- #[inline]
- pub fn bottom(&self, mode: WritingMode) -> T {
- self.debug_writing_mode.check(mode);
- if mode.is_vertical() {
- if mode.is_inline_tb() {
- self.inline_end
- } else {
- self.inline_start
- }
- } else {
- self.block_end
- }
- }
-
- #[inline]
- pub fn set_bottom(&mut self, mode: WritingMode, bottom: T) {
- self.debug_writing_mode.check(mode);
- if mode.is_vertical() {
- if mode.is_inline_tb() {
- self.inline_end = bottom
- } else {
- self.inline_start = bottom
- }
- } else {
- self.block_end = bottom
- }
- }
-
- #[inline]
- pub fn left(&self, mode: WritingMode) -> T {
- self.debug_writing_mode.check(mode);
- if mode.is_vertical() {
- if mode.is_vertical_lr() {
- self.block_start
- } else {
- self.block_end
- }
- } else {
- if mode.is_bidi_ltr() {
- self.inline_start
- } else {
- self.inline_end
- }
- }
- }
-
- #[inline]
- pub fn set_left(&mut self, mode: WritingMode, left: T) {
- self.debug_writing_mode.check(mode);
- if mode.is_vertical() {
- if mode.is_vertical_lr() {
- self.block_start = left
- } else {
- self.block_end = left
- }
- } else {
- if mode.is_bidi_ltr() {
- self.inline_start = left
- } else {
- self.inline_end = left
- }
- }
- }
-
- #[inline]
- pub fn convert(&self, mode_from: WritingMode, mode_to: WritingMode) -> LogicalMargin<T> {
- if mode_from == mode_to {
- self.debug_writing_mode.check(mode_from);
- *self
- } else {
- LogicalMargin::from_physical(mode_to, self.to_physical(mode_from))
- }
- }
-}
-
-impl<T: Clone> LogicalMargin<T> {
- #[inline]
- pub fn to_physical(&self, mode: WritingMode) -> SideOffsets2D<T> {
- self.debug_writing_mode.check(mode);
- let top;
- let right;
- let bottom;
- let left;
- if mode.is_vertical() {
- if mode.is_vertical_lr() {
- left = self.block_start.clone();
- right = self.block_end.clone();
- } else {
- right = self.block_start.clone();
- left = self.block_end.clone();
- }
- if mode.is_inline_tb() {
- top = self.inline_start.clone();
- bottom = self.inline_end.clone();
- } else {
- bottom = self.inline_start.clone();
- top = self.inline_end.clone();
- }
- } else {
- top = self.block_start.clone();
- bottom = self.block_end.clone();
- if mode.is_bidi_ltr() {
- left = self.inline_start.clone();
- right = self.inline_end.clone();
- } else {
- right = self.inline_start.clone();
- left = self.inline_end.clone();
- }
- }
- SideOffsets2D::new(top, right, bottom, left)
- }
-}
-
-impl<T: PartialEq + Zero> LogicalMargin<T> {
- #[inline]
- pub fn is_zero(&self) -> bool {
- self.block_start == Zero::zero() &&
- self.inline_end == Zero::zero() &&
- self.block_end == Zero::zero() &&
- self.inline_start == Zero::zero()
- }
-}
-
-impl<T: Copy + Add<T, Output = T>> LogicalMargin<T> {
- #[inline]
- pub fn inline_start_end(&self) -> T {
- self.inline_start + self.inline_end
- }
-
- #[inline]
- pub fn block_start_end(&self) -> T {
- self.block_start + self.block_end
- }
-
- #[inline]
- pub fn start_end(&self, direction: Direction) -> T {
- match direction {
- Direction::Inline => self.inline_start + self.inline_end,
- Direction::Block => self.block_start + self.block_end,
- }
- }
-
- #[inline]
- pub fn top_bottom(&self, mode: WritingMode) -> T {
- self.debug_writing_mode.check(mode);
- if mode.is_vertical() {
- self.inline_start_end()
- } else {
- self.block_start_end()
- }
- }
-
- #[inline]
- pub fn left_right(&self, mode: WritingMode) -> T {
- self.debug_writing_mode.check(mode);
- if mode.is_vertical() {
- self.block_start_end()
- } else {
- self.inline_start_end()
- }
- }
-}
-
-impl<T: Add<T, Output = T>> Add for LogicalMargin<T> {
- type Output = LogicalMargin<T>;
-
- #[inline]
- fn add(self, other: LogicalMargin<T>) -> LogicalMargin<T> {
- self.debug_writing_mode
- .check_debug(other.debug_writing_mode);
- LogicalMargin {
- debug_writing_mode: self.debug_writing_mode,
- block_start: self.block_start + other.block_start,
- inline_end: self.inline_end + other.inline_end,
- block_end: self.block_end + other.block_end,
- inline_start: self.inline_start + other.inline_start,
- }
- }
-}
-
-impl<T: Sub<T, Output = T>> Sub for LogicalMargin<T> {
- type Output = LogicalMargin<T>;
-
- #[inline]
- fn sub(self, other: LogicalMargin<T>) -> LogicalMargin<T> {
- self.debug_writing_mode
- .check_debug(other.debug_writing_mode);
- LogicalMargin {
- debug_writing_mode: self.debug_writing_mode,
- block_start: self.block_start - other.block_start,
- inline_end: self.inline_end - other.inline_end,
- block_end: self.block_end - other.block_end,
- inline_start: self.inline_start - other.inline_start,
- }
- }
-}
-
-/// A rectangle in flow-relative dimensions
-#[derive(Clone, Copy, Eq, PartialEq)]
-#[cfg_attr(feature = "servo", derive(Serialize))]
-pub struct LogicalRect<T> {
- pub start: LogicalPoint<T>,
- pub size: LogicalSize<T>,
- debug_writing_mode: DebugWritingMode,
-}
-
-impl<T: Debug> Debug for LogicalRect<T> {
- fn fmt(&self, formatter: &mut Formatter) -> Result<(), Error> {
- let writing_mode_string = if cfg!(debug_assertions) {
- format!("{:?}, ", self.debug_writing_mode)
- } else {
- "".to_owned()
- };
-
- write!(
- formatter,
- "LogicalRect({}i{:?}×b{:?}, @ (i{:?},b{:?}))",
- writing_mode_string, self.size.inline, self.size.block, self.start.i, self.start.b
- )
- }
-}
-
-impl<T: Zero> LogicalRect<T> {
- #[inline]
- pub fn zero(mode: WritingMode) -> LogicalRect<T> {
- LogicalRect {
- start: LogicalPoint::zero(mode),
- size: LogicalSize::zero(mode),
- debug_writing_mode: DebugWritingMode::new(mode),
- }
- }
-}
-
-impl<T: Copy> LogicalRect<T> {
- #[inline]
- pub fn new(
- mode: WritingMode,
- inline_start: T,
- block_start: T,
- inline: T,
- block: T,
- ) -> LogicalRect<T> {
- LogicalRect {
- start: LogicalPoint::new(mode, inline_start, block_start),
- size: LogicalSize::new(mode, inline, block),
- debug_writing_mode: DebugWritingMode::new(mode),
- }
- }
-
- #[inline]
- pub fn from_point_size(
- mode: WritingMode,
- start: LogicalPoint<T>,
- size: LogicalSize<T>,
- ) -> LogicalRect<T> {
- start.debug_writing_mode.check(mode);
- size.debug_writing_mode.check(mode);
- LogicalRect {
- start: start,
- size: size,
- debug_writing_mode: DebugWritingMode::new(mode),
- }
- }
-}
-
-impl<T: Copy + Add<T, Output = T> + Sub<T, Output = T>> LogicalRect<T> {
- #[inline]
- pub fn from_physical(
- mode: WritingMode,
- rect: Rect<T>,
- container_size: Size2D<T>,
- ) -> LogicalRect<T> {
- let inline_start;
- let block_start;
- let inline;
- let block;
- if mode.is_vertical() {
- inline = rect.size.height;
- block = rect.size.width;
- if mode.is_vertical_lr() {
- block_start = rect.origin.x;
- } else {
- block_start = container_size.width - (rect.origin.x + rect.size.width);
- }
- if mode.is_inline_tb() {
- inline_start = rect.origin.y;
- } else {
- inline_start = container_size.height - (rect.origin.y + rect.size.height);
- }
- } else {
- inline = rect.size.width;
- block = rect.size.height;
- block_start = rect.origin.y;
- if mode.is_bidi_ltr() {
- inline_start = rect.origin.x;
- } else {
- inline_start = container_size.width - (rect.origin.x + rect.size.width);
- }
- }
- LogicalRect {
- start: LogicalPoint::new(mode, inline_start, block_start),
- size: LogicalSize::new(mode, inline, block),
- debug_writing_mode: DebugWritingMode::new(mode),
- }
- }
-
- #[inline]
- pub fn inline_end(&self) -> T {
- self.start.i + self.size.inline
- }
-
- #[inline]
- pub fn block_end(&self) -> T {
- self.start.b + self.size.block
- }
-
- #[inline]
- pub fn to_physical(&self, mode: WritingMode, container_size: Size2D<T>) -> Rect<T> {
- self.debug_writing_mode.check(mode);
- let x;
- let y;
- let width;
- let height;
- if mode.is_vertical() {
- width = self.size.block;
- height = self.size.inline;
- if mode.is_vertical_lr() {
- x = self.start.b;
- } else {
- x = container_size.width - self.block_end();
- }
- if mode.is_inline_tb() {
- y = self.start.i;
- } else {
- y = container_size.height - self.inline_end();
- }
- } else {
- width = self.size.inline;
- height = self.size.block;
- y = self.start.b;
- if mode.is_bidi_ltr() {
- x = self.start.i;
- } else {
- x = container_size.width - self.inline_end();
- }
- }
- Rect {
- origin: Point2D::new(x, y),
- size: Size2D::new(width, height),
- }
- }
-
- #[inline]
- pub fn convert(
- &self,
- mode_from: WritingMode,
- mode_to: WritingMode,
- container_size: Size2D<T>,
- ) -> LogicalRect<T> {
- if mode_from == mode_to {
- self.debug_writing_mode.check(mode_from);
- *self
- } else {
- LogicalRect::from_physical(
- mode_to,
- self.to_physical(mode_from, container_size),
- container_size,
- )
- }
- }
-
- pub fn translate_by_size(&self, offset: LogicalSize<T>) -> LogicalRect<T> {
- LogicalRect {
- start: self.start + offset,
- ..*self
- }
- }
-
- pub fn translate(&self, offset: &LogicalPoint<T>) -> LogicalRect<T> {
- LogicalRect {
- start: self.start +
- LogicalSize {
- inline: offset.i,
- block: offset.b,
- debug_writing_mode: offset.debug_writing_mode,
- },
- size: self.size,
- debug_writing_mode: self.debug_writing_mode,
- }
- }
-}
-
-impl<T: Copy + Ord + Add<T, Output = T> + Sub<T, Output = T>> LogicalRect<T> {
- #[inline]
- pub fn union(&self, other: &LogicalRect<T>) -> LogicalRect<T> {
- self.debug_writing_mode
- .check_debug(other.debug_writing_mode);
-
- let inline_start = min(self.start.i, other.start.i);
- let block_start = min(self.start.b, other.start.b);
- LogicalRect {
- start: LogicalPoint {
- i: inline_start,
- b: block_start,
- debug_writing_mode: self.debug_writing_mode,
- },
- size: LogicalSize {
- inline: max(self.inline_end(), other.inline_end()) - inline_start,
- block: max(self.block_end(), other.block_end()) - block_start,
- debug_writing_mode: self.debug_writing_mode,
- },
- debug_writing_mode: self.debug_writing_mode,
- }
- }
-}
-
-impl<T: Copy + Add<T, Output = T> + Sub<T, Output = T>> Add<LogicalMargin<T>> for LogicalRect<T> {
- type Output = LogicalRect<T>;
-
- #[inline]
- fn add(self, other: LogicalMargin<T>) -> LogicalRect<T> {
- self.debug_writing_mode
- .check_debug(other.debug_writing_mode);
- LogicalRect {
- start: LogicalPoint {
- // Growing a rectangle on the start side means pushing its
- // start point on the negative direction.
- i: self.start.i - other.inline_start,
- b: self.start.b - other.block_start,
- debug_writing_mode: self.debug_writing_mode,
- },
- size: LogicalSize {
- inline: self.size.inline + other.inline_start_end(),
- block: self.size.block + other.block_start_end(),
- debug_writing_mode: self.debug_writing_mode,
- },
- debug_writing_mode: self.debug_writing_mode,
- }
- }
-}
-
-impl<T: Copy + Add<T, Output = T> + Sub<T, Output = T>> Sub<LogicalMargin<T>> for LogicalRect<T> {
- type Output = LogicalRect<T>;
-
- #[inline]
- fn sub(self, other: LogicalMargin<T>) -> LogicalRect<T> {
- self.debug_writing_mode
- .check_debug(other.debug_writing_mode);
- LogicalRect {
- start: LogicalPoint {
- // Shrinking a rectangle on the start side means pushing its
- // start point on the positive direction.
- i: self.start.i + other.inline_start,
- b: self.start.b + other.block_start,
- debug_writing_mode: self.debug_writing_mode,
- },
- size: LogicalSize {
- inline: self.size.inline - other.inline_start_end(),
- block: self.size.block - other.block_start_end(),
- debug_writing_mode: self.debug_writing_mode,
- },
- debug_writing_mode: self.debug_writing_mode,
- }
- }
-}
-
-#[derive(Clone, Copy, Debug, PartialEq)]
-pub enum PhysicalSide {
- Top,
- Right,
- Bottom,
- Left,
-}
-
-#[derive(Clone, Copy, Debug, PartialEq)]
-pub enum PhysicalCorner {
- TopLeft,
- TopRight,
- BottomRight,
- BottomLeft,
-}
diff --git a/components/style/macros.rs b/components/style/macros.rs
deleted file mode 100644
index 5f3a1ea463b..00000000000
--- a/components/style/macros.rs
+++ /dev/null
@@ -1,98 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
-
-//! Various macro helpers.
-
-macro_rules! exclusive_value {
- (($value:ident, $set:expr) => $ident:path) => {
- if $value.intersects($set) {
- return Err(());
- } else {
- $ident
- }
- };
-}
-
-#[cfg(feature = "gecko")]
-macro_rules! impl_gecko_keyword_conversions {
- ($name:ident, $utype:ty) => {
- impl From<$utype> for $name {
- fn from(bits: $utype) -> $name {
- $name::from_gecko_keyword(bits)
- }
- }
-
- impl From<$name> for $utype {
- fn from(v: $name) -> $utype {
- v.to_gecko_keyword()
- }
- }
- };
-}
-
-macro_rules! trivial_to_computed_value {
- ($name:ty) => {
- impl $crate::values::computed::ToComputedValue for $name {
- type ComputedValue = $name;
-
- fn to_computed_value(&self, _: &$crate::values::computed::Context) -> Self {
- self.clone()
- }
-
- fn from_computed_value(other: &Self) -> Self {
- other.clone()
- }
- }
- };
-}
-
-/// A macro to parse an identifier, or return an `UnexpectedIdent` error
-/// otherwise.
-///
-/// FIXME(emilio): The fact that `UnexpectedIdent` is a `SelectorParseError`
-/// doesn't make a lot of sense to me.
-macro_rules! try_match_ident_ignore_ascii_case {
- ($input:expr, $( $match_body:tt )*) => {{
- let location = $input.current_source_location();
- let ident = $input.expect_ident_cloned()?;
- match_ignore_ascii_case! { &ident,
- $( $match_body )*
- _ => return Err(location.new_custom_error(
- ::selectors::parser::SelectorParseErrorKind::UnexpectedIdent(ident.clone())
- ))
- }
- }}
-}
-
-#[cfg(feature = "servo")]
-macro_rules! local_name {
- ($s:tt) => {
- $crate::values::GenericAtomIdent(html5ever::local_name!($s))
- };
-}
-
-#[cfg(feature = "servo")]
-macro_rules! ns {
- () => {
- $crate::values::GenericAtomIdent(html5ever::ns!())
- };
- ($s:tt) => {
- $crate::values::GenericAtomIdent(html5ever::ns!($s))
- };
-}
-
-#[cfg(feature = "gecko")]
-macro_rules! local_name {
- ($s:tt) => {
- $crate::values::AtomIdent(atom!($s))
- };
-}
-
-/// Asserts the size of a type at compile time.
-macro_rules! size_of_test {
- ($t: ty, $expected_size: expr) => {
- #[cfg(target_pointer_width = "64")]
- const_assert_eq!(std::mem::size_of::<$t>(), $expected_size);
- };
-}
diff --git a/components/style/matching.rs b/components/style/matching.rs
deleted file mode 100644
index bc9c797a5d2..00000000000
--- a/components/style/matching.rs
+++ /dev/null
@@ -1,1096 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
-
-//! High-level interface to CSS selector matching.
-
-#![allow(unsafe_code)]
-#![deny(missing_docs)]
-
-use crate::computed_value_flags::ComputedValueFlags;
-use crate::context::{CascadeInputs, ElementCascadeInputs, QuirksMode};
-use crate::context::{SharedStyleContext, StyleContext};
-use crate::data::{ElementData, ElementStyles};
-use crate::dom::TElement;
-#[cfg(feature = "servo")]
-use crate::dom::TNode;
-use crate::invalidation::element::restyle_hints::RestyleHint;
-use crate::properties::longhands::display::computed_value::T as Display;
-use crate::properties::ComputedValues;
-use crate::properties::PropertyDeclarationBlock;
-use crate::rule_tree::{CascadeLevel, StrongRuleNode};
-use crate::selector_parser::{PseudoElement, RestyleDamage};
-use crate::shared_lock::Locked;
-use crate::style_resolver::ResolvedElementStyles;
-use crate::style_resolver::{PseudoElementResolution, StyleResolverForElement};
-use crate::stylesheets::layer_rule::LayerOrder;
-use crate::stylist::RuleInclusion;
-use crate::traversal_flags::TraversalFlags;
-use servo_arc::{Arc, ArcBorrow};
-
-/// Represents the result of comparing an element's old and new style.
-#[derive(Debug)]
-pub struct StyleDifference {
- /// The resulting damage.
- pub damage: RestyleDamage,
-
- /// Whether any styles changed.
- pub change: StyleChange,
-}
-
-/// Represents whether or not the style of an element has changed.
-#[derive(Clone, Copy, Debug)]
-pub enum StyleChange {
- /// The style hasn't changed.
- Unchanged,
- /// The style has changed.
- Changed {
- /// Whether only reset structs changed.
- reset_only: bool,
- },
-}
-
-/// Whether or not newly computed values for an element need to be cascaded to
-/// children (or children might need to be re-matched, e.g., for container
-/// queries).
-#[derive(Clone, Copy, Debug, Eq, Ord, PartialEq, PartialOrd)]
-pub enum ChildRestyleRequirement {
- /// Old and new computed values were the same, or we otherwise know that
- /// we won't bother recomputing style for children, so we can skip cascading
- /// the new values into child elements.
- CanSkipCascade = 0,
- /// The same as `MustCascadeChildren`, but we only need to actually
- /// recascade if the child inherits any explicit reset style.
- MustCascadeChildrenIfInheritResetStyle = 1,
- /// Old and new computed values were different, so we must cascade the
- /// new values to children.
- MustCascadeChildren = 2,
- /// The same as `MustCascadeChildren`, but for the entire subtree. This is
- /// used to handle root font-size updates needing to recascade the whole
- /// document.
- MustCascadeDescendants = 3,
- /// We need to re-match the whole subttree. This is used to handle container
- /// query relative unit changes for example. Container query size changes
- /// also trigger re-match, but after layout.
- MustMatchDescendants = 4,
-}
-
-/// Determines which styles are being cascaded currently.
-#[derive(Clone, Copy, Debug, Eq, PartialEq)]
-enum CascadeVisitedMode {
- /// Cascade the regular, unvisited styles.
- Unvisited,
- /// Cascade the styles used when an element's relevant link is visited. A
- /// "relevant link" is the element being matched if it is a link or the
- /// nearest ancestor link.
- Visited,
-}
-
-trait PrivateMatchMethods: TElement {
- fn replace_single_rule_node(
- context: &SharedStyleContext,
- level: CascadeLevel,
- layer_order: LayerOrder,
- pdb: Option<ArcBorrow<Locked<PropertyDeclarationBlock>>>,
- path: &mut StrongRuleNode,
- ) -> bool {
- let stylist = &context.stylist;
- let guards = &context.guards;
-
- let mut important_rules_changed = false;
- let new_node = stylist.rule_tree().update_rule_at_level(
- level,
- layer_order,
- pdb,
- path,
- guards,
- &mut important_rules_changed,
- );
- if let Some(n) = new_node {
- *path = n;
- }
- important_rules_changed
- }
-
- /// Updates the rule nodes without re-running selector matching, using just
- /// the rule tree, for a specific visited mode.
- ///
- /// Returns true if an !important rule was replaced.
- fn replace_rules_internal(
- &self,
- replacements: RestyleHint,
- context: &mut StyleContext<Self>,
- cascade_visited: CascadeVisitedMode,
- cascade_inputs: &mut ElementCascadeInputs,
- ) -> bool {
- debug_assert!(
- replacements.intersects(RestyleHint::replacements()) &&
- (replacements & !RestyleHint::replacements()).is_empty()
- );
-
- let primary_rules = match cascade_visited {
- CascadeVisitedMode::Unvisited => cascade_inputs.primary.rules.as_mut(),
- CascadeVisitedMode::Visited => cascade_inputs.primary.visited_rules.as_mut(),
- };
-
- let primary_rules = match primary_rules {
- Some(r) => r,
- None => return false,
- };
-
- if !context.shared.traversal_flags.for_animation_only() {
- let mut result = false;
- if replacements.contains(RestyleHint::RESTYLE_STYLE_ATTRIBUTE) {
- let style_attribute = self.style_attribute();
- result |= Self::replace_single_rule_node(
- context.shared,
- CascadeLevel::same_tree_author_normal(),
- LayerOrder::root(),
- style_attribute,
- primary_rules,
- );
- result |= Self::replace_single_rule_node(
- context.shared,
- CascadeLevel::same_tree_author_important(),
- LayerOrder::root(),
- style_attribute,
- primary_rules,
- );
- // FIXME(emilio): Still a hack!
- self.unset_dirty_style_attribute();
- }
- return result;
- }
-
- // Animation restyle hints are processed prior to other restyle
- // hints in the animation-only traversal.
- //
- // Non-animation restyle hints will be processed in a subsequent
- // normal traversal.
- if replacements.intersects(RestyleHint::for_animations()) {
- debug_assert!(context.shared.traversal_flags.for_animation_only());
-
- if replacements.contains(RestyleHint::RESTYLE_SMIL) {
- Self::replace_single_rule_node(
- context.shared,
- CascadeLevel::SMILOverride,
- LayerOrder::root(),
- self.smil_override(),
- primary_rules,
- );
- }
-
- if replacements.contains(RestyleHint::RESTYLE_CSS_TRANSITIONS) {
- Self::replace_single_rule_node(
- context.shared,
- CascadeLevel::Transitions,
- LayerOrder::root(),
- self.transition_rule(&context.shared)
- .as_ref()
- .map(|a| a.borrow_arc()),
- primary_rules,
- );
- }
-
- if replacements.contains(RestyleHint::RESTYLE_CSS_ANIMATIONS) {
- Self::replace_single_rule_node(
- context.shared,
- CascadeLevel::Animations,
- LayerOrder::root(),
- self.animation_rule(&context.shared)
- .as_ref()
- .map(|a| a.borrow_arc()),
- primary_rules,
- );
- }
- }
-
- false
- }
-
- /// If there is no transition rule in the ComputedValues, it returns None.
- fn after_change_style(
- &self,
- context: &mut StyleContext<Self>,
- primary_style: &Arc<ComputedValues>,
- ) -> Option<Arc<ComputedValues>> {
- let rule_node = primary_style.rules();
- let without_transition_rules = context
- .shared
- .stylist
- .rule_tree()
- .remove_transition_rule_if_applicable(rule_node);
- if without_transition_rules == *rule_node {
- // We don't have transition rule in this case, so return None to let
- // the caller use the original ComputedValues.
- return None;
- }
-
- // FIXME(bug 868975): We probably need to transition visited style as
- // well.
- let inputs = CascadeInputs {
- rules: Some(without_transition_rules),
- visited_rules: primary_style.visited_rules().cloned(),
- flags: primary_style.flags.for_cascade_inputs(),
- };
-
- // Actually `PseudoElementResolution` doesn't really matter.
- let style = StyleResolverForElement::new(
- *self,
- context,
- RuleInclusion::All,
- PseudoElementResolution::IfApplicable,
- )
- .cascade_style_and_visited_with_default_parents(inputs);
-
- Some(style.0)
- }
-
- fn needs_animations_update(
- &self,
- context: &mut StyleContext<Self>,
- old_style: Option<&ComputedValues>,
- new_style: &ComputedValues,
- pseudo_element: Option<PseudoElement>,
- ) -> bool {
- let new_ui_style = new_style.get_ui();
- let new_style_specifies_animations = new_ui_style.specifies_animations();
-
- let has_animations = self.has_css_animations(&context.shared, pseudo_element);
- if !new_style_specifies_animations && !has_animations {
- return false;
- }
-
- let old_style = match old_style {
- Some(old) => old,
- // If we have no old style but have animations, we may be a
- // pseudo-element which was re-created without style changes.
- //
- // This can happen when we reframe the pseudo-element without
- // restyling it (due to content insertion on a flex container or
- // such, for example). See bug 1564366.
- //
- // FIXME(emilio): The really right fix for this is keeping the
- // pseudo-element itself around on reframes, but that's a bit
- // harder. If we do that we can probably remove quite a lot of the
- // EffectSet complexity though, since right now it's stored on the
- // parent element for pseudo-elements given we need to keep it
- // around...
- None => {
- return new_style_specifies_animations || new_style.is_pseudo_style();
- },
- };
-
- let old_ui_style = old_style.get_ui();
-
- let keyframes_could_have_changed = context
- .shared
- .traversal_flags
- .contains(TraversalFlags::ForCSSRuleChanges);
-
- // If the traversal is triggered due to changes in CSS rules changes, we
- // need to try to update all CSS animations on the element if the
- // element has or will have CSS animation style regardless of whether
- // the animation is running or not.
- //
- // TODO: We should check which @keyframes were added/changed/deleted and
- // update only animations corresponding to those @keyframes.
- if keyframes_could_have_changed {
- return true;
- }
-
- // If the animations changed, well...
- if !old_ui_style.animations_equals(new_ui_style) {
- return true;
- }
-
- let old_display = old_style.clone_display();
- let new_display = new_style.clone_display();
-
- // If we were display: none, we may need to trigger animations.
- if old_display == Display::None && new_display != Display::None {
- return new_style_specifies_animations;
- }
-
- // If we are becoming display: none, we may need to stop animations.
- if old_display != Display::None && new_display == Display::None {
- return has_animations;
- }
-
- // We might need to update animations if writing-mode or direction
- // changed, and any of the animations contained logical properties.
- //
- // We may want to be more granular, but it's probably not worth it.
- if new_style.writing_mode != old_style.writing_mode {
- return has_animations;
- }
-
- false
- }
-
- fn might_need_transitions_update(
- &self,
- context: &StyleContext<Self>,
- old_style: Option<&ComputedValues>,
- new_style: &ComputedValues,
- pseudo_element: Option<PseudoElement>,
- ) -> bool {
- let old_style = match old_style {
- Some(v) => v,
- None => return false,
- };
-
- if !self.has_css_transitions(context.shared, pseudo_element) &&
- !new_style.get_ui().specifies_transitions()
- {
- return false;
- }
-
- if old_style.clone_display().is_none() {
- return false;
- }
-
- return true;
- }
-
- /// Create a SequentialTask for resolving descendants in a SMIL display
- /// property animation if the display property changed from none.
- #[cfg(feature = "gecko")]
- fn handle_display_change_for_smil_if_needed(
- &self,
- context: &mut StyleContext<Self>,
- old_values: Option<&ComputedValues>,
- new_values: &ComputedValues,
- restyle_hints: RestyleHint,
- ) {
- use crate::context::PostAnimationTasks;
-
- if !restyle_hints.intersects(RestyleHint::RESTYLE_SMIL) {
- return;
- }
-
- if new_values.is_display_property_changed_from_none(old_values) {
- // When display value is changed from none to other, we need to
- // traverse descendant elements in a subsequent normal
- // traversal (we can't traverse them in this animation-only restyle
- // since we have no way to know whether the decendants
- // need to be traversed at the beginning of the animation-only
- // restyle).
- let task = crate::context::SequentialTask::process_post_animation(
- *self,
- PostAnimationTasks::DISPLAY_CHANGED_FROM_NONE_FOR_SMIL,
- );
- context.thread_local.tasks.push(task);
- }
- }
-
- #[cfg(feature = "gecko")]
- fn process_animations(
- &self,
- context: &mut StyleContext<Self>,
- old_styles: &mut ElementStyles,
- new_styles: &mut ResolvedElementStyles,
- restyle_hint: RestyleHint,
- important_rules_changed: bool,
- ) {
- use crate::context::UpdateAnimationsTasks;
-
- let new_values = new_styles.primary_style_mut();
- let old_values = &old_styles.primary;
- if context.shared.traversal_flags.for_animation_only() {
- self.handle_display_change_for_smil_if_needed(
- context,
- old_values.as_deref(),
- new_values,
- restyle_hint,
- );
- return;
- }
-
- // Bug 868975: These steps should examine and update the visited styles
- // in addition to the unvisited styles.
-
- let mut tasks = UpdateAnimationsTasks::empty();
-
- if old_values.as_deref().map_or_else(
- || new_values.get_ui().specifies_scroll_timelines(),
- |old| !old.get_ui().scroll_timelines_equals(new_values.get_ui()),
- ) {
- tasks.insert(UpdateAnimationsTasks::SCROLL_TIMELINES);
- }
-
- if old_values.as_deref().map_or_else(
- || new_values.get_ui().specifies_view_timelines(),
- |old| !old.get_ui().view_timelines_equals(new_values.get_ui()),
- ) {
- tasks.insert(UpdateAnimationsTasks::VIEW_TIMELINES);
- }
-
- if self.needs_animations_update(
- context,
- old_values.as_deref(),
- new_values,
- /* pseudo_element = */ None,
- ) {
- tasks.insert(UpdateAnimationsTasks::CSS_ANIMATIONS);
- }
-
- let before_change_style = if self.might_need_transitions_update(
- context,
- old_values.as_deref(),
- new_values,
- /* pseudo_element = */ None,
- ) {
- let after_change_style =
- if self.has_css_transitions(context.shared, /* pseudo_element = */ None) {
- self.after_change_style(context, new_values)
- } else {
- None
- };
-
- // In order to avoid creating a SequentialTask for transitions which
- // may not be updated, we check it per property to make sure Gecko
- // side will really update transition.
- let needs_transitions_update = {
- // We borrow new_values here, so need to add a scope to make
- // sure we release it before assigning a new value to it.
- let after_change_style_ref = after_change_style.as_ref().unwrap_or(&new_values);
-
- self.needs_transitions_update(old_values.as_ref().unwrap(), after_change_style_ref)
- };
-
- if needs_transitions_update {
- if let Some(values_without_transitions) = after_change_style {
- *new_values = values_without_transitions;
- }
- tasks.insert(UpdateAnimationsTasks::CSS_TRANSITIONS);
-
- // We need to clone old_values into SequentialTask, so we can
- // use it later.
- old_values.clone()
- } else {
- None
- }
- } else {
- None
- };
-
- if self.has_animations(&context.shared) {
- tasks.insert(UpdateAnimationsTasks::EFFECT_PROPERTIES);
- if important_rules_changed {
- tasks.insert(UpdateAnimationsTasks::CASCADE_RESULTS);
- }
- if new_values.is_display_property_changed_from_none(old_values.as_deref()) {
- tasks.insert(UpdateAnimationsTasks::DISPLAY_CHANGED_FROM_NONE);
- }
- }
-
- if !tasks.is_empty() {
- let task = crate::context::SequentialTask::update_animations(
- *self,
- before_change_style,
- tasks,
- );
- context.thread_local.tasks.push(task);
- }
- }
-
- #[cfg(feature = "servo")]
- fn process_animations(
- &self,
- context: &mut StyleContext<Self>,
- old_styles: &mut ElementStyles,
- new_resolved_styles: &mut ResolvedElementStyles,
- _restyle_hint: RestyleHint,
- _important_rules_changed: bool,
- ) {
- use crate::animation::AnimationSetKey;
- use crate::dom::TDocument;
-
- let style_changed = self.process_animations_for_style(
- context,
- &mut old_styles.primary,
- new_resolved_styles.primary_style_mut(),
- /* pseudo_element = */ None,
- );
-
- // If we have modified animation or transitions, we recascade style for this node.
- if style_changed {
- let primary_style = new_resolved_styles.primary_style();
- let mut rule_node = primary_style.rules().clone();
- let declarations = context.shared.animations.get_all_declarations(
- &AnimationSetKey::new_for_non_pseudo(self.as_node().opaque()),
- context.shared.current_time_for_animations,
- self.as_node().owner_doc().shared_lock(),
- );
- Self::replace_single_rule_node(
- &context.shared,
- CascadeLevel::Transitions,
- LayerOrder::root(),
- declarations.transitions.as_ref().map(|a| a.borrow_arc()),
- &mut rule_node,
- );
- Self::replace_single_rule_node(
- &context.shared,
- CascadeLevel::Animations,
- LayerOrder::root(),
- declarations.animations.as_ref().map(|a| a.borrow_arc()),
- &mut rule_node,
- );
-
- if rule_node != *primary_style.rules() {
- let inputs = CascadeInputs {
- rules: Some(rule_node),
- visited_rules: primary_style.visited_rules().cloned(),
- flags: primary_style.flags.for_cascade_inputs(),
- };
-
- new_resolved_styles.primary.style = StyleResolverForElement::new(
- *self,
- context,
- RuleInclusion::All,
- PseudoElementResolution::IfApplicable,
- )
- .cascade_style_and_visited_with_default_parents(inputs);
- }
- }
-
- self.process_animations_for_pseudo(
- context,
- old_styles,
- new_resolved_styles,
- PseudoElement::Before,
- );
- self.process_animations_for_pseudo(
- context,
- old_styles,
- new_resolved_styles,
- PseudoElement::After,
- );
- }
-
- #[cfg(feature = "servo")]
- fn process_animations_for_pseudo(
- &self,
- context: &mut StyleContext<Self>,
- old_styles: &mut ElementStyles,
- new_resolved_styles: &mut ResolvedElementStyles,
- pseudo_element: PseudoElement,
- ) {
- use crate::animation::AnimationSetKey;
- use crate::dom::TDocument;
-
- let key = AnimationSetKey::new_for_pseudo(self.as_node().opaque(), pseudo_element.clone());
- let mut style = match new_resolved_styles.pseudos.get(&pseudo_element) {
- Some(style) => Arc::clone(style),
- None => {
- context
- .shared
- .animations
- .cancel_all_animations_for_key(&key);
- return;
- },
- };
-
- let mut old_style = old_styles.pseudos.get(&pseudo_element).cloned();
- self.process_animations_for_style(
- context,
- &mut old_style,
- &mut style,
- Some(pseudo_element.clone()),
- );
-
- let declarations = context.shared.animations.get_all_declarations(
- &key,
- context.shared.current_time_for_animations,
- self.as_node().owner_doc().shared_lock(),
- );
- if declarations.is_empty() {
- return;
- }
-
- let mut rule_node = style.rules().clone();
- Self::replace_single_rule_node(
- &context.shared,
- CascadeLevel::Transitions,
- LayerOrder::root(),
- declarations.transitions.as_ref().map(|a| a.borrow_arc()),
- &mut rule_node,
- );
- Self::replace_single_rule_node(
- &context.shared,
- CascadeLevel::Animations,
- LayerOrder::root(),
- declarations.animations.as_ref().map(|a| a.borrow_arc()),
- &mut rule_node,
- );
- if rule_node == *style.rules() {
- return;
- }
-
- let inputs = CascadeInputs {
- rules: Some(rule_node),
- visited_rules: style.visited_rules().cloned(),
- flags: style.flags.for_cascade_inputs(),
- };
-
- let new_style = StyleResolverForElement::new(
- *self,
- context,
- RuleInclusion::All,
- PseudoElementResolution::IfApplicable,
- )
- .cascade_style_and_visited_for_pseudo_with_default_parents(
- inputs,
- &pseudo_element,
- &new_resolved_styles.primary,
- );
-
- new_resolved_styles
- .pseudos
- .set(&pseudo_element, new_style.0);
- }
-
- #[cfg(feature = "servo")]
- fn process_animations_for_style(
- &self,
- context: &mut StyleContext<Self>,
- old_values: &mut Option<Arc<ComputedValues>>,
- new_values: &mut Arc<ComputedValues>,
- pseudo_element: Option<PseudoElement>,
- ) -> bool {
- use crate::animation::{AnimationSetKey, AnimationState};
-
- // We need to call this before accessing the `ElementAnimationSet` from the
- // map because this call will do a RwLock::read().
- let needs_animations_update = self.needs_animations_update(
- context,
- old_values.as_deref(),
- new_values,
- pseudo_element,
- );
-
- let might_need_transitions_update = self.might_need_transitions_update(
- context,
- old_values.as_deref(),
- new_values,
- pseudo_element,
- );
-
- let mut after_change_style = None;
- if might_need_transitions_update {
- after_change_style = self.after_change_style(context, new_values);
- }
-
- let key = AnimationSetKey::new(self.as_node().opaque(), pseudo_element);
- let shared_context = context.shared;
- let mut animation_set = shared_context
- .animations
- .sets
- .write()
- .remove(&key)
- .unwrap_or_default();
-
- // Starting animations is expensive, because we have to recalculate the style
- // for all the keyframes. We only want to do this if we think that there's a
- // chance that the animations really changed.
- if needs_animations_update {
- let mut resolver = StyleResolverForElement::new(
- *self,
- context,
- RuleInclusion::All,
- PseudoElementResolution::IfApplicable,
- );
-
- animation_set.update_animations_for_new_style::<Self>(
- *self,
- &shared_context,
- &new_values,
- &mut resolver,
- );
- }
-
- animation_set.update_transitions_for_new_style(
- might_need_transitions_update,
- &shared_context,
- old_values.as_ref(),
- after_change_style.as_ref().unwrap_or(new_values),
- );
-
- // We clear away any finished transitions, but retain animations, because they
- // might still be used for proper calculation of `animation-fill-mode`. This
- // should change the computed values in the style, so we don't need to mark
- // this set as dirty.
- animation_set
- .transitions
- .retain(|transition| transition.state != AnimationState::Finished);
-
- // If the ElementAnimationSet is empty, and don't store it in order to
- // save memory and to avoid extra processing later.
- let changed_animations = animation_set.dirty;
- if !animation_set.is_empty() {
- animation_set.dirty = false;
- shared_context
- .animations
- .sets
- .write()
- .insert(key, animation_set);
- }
-
- changed_animations
- }
-
- /// Computes and applies non-redundant damage.
- fn accumulate_damage_for(
- &self,
- shared_context: &SharedStyleContext,
- damage: &mut RestyleDamage,
- old_values: &ComputedValues,
- new_values: &ComputedValues,
- pseudo: Option<&PseudoElement>,
- ) -> ChildRestyleRequirement {
- debug!("accumulate_damage_for: {:?}", self);
- debug_assert!(!shared_context
- .traversal_flags
- .contains(TraversalFlags::FinalAnimationTraversal));
-
- let difference = self.compute_style_difference(old_values, new_values, pseudo);
-
- *damage |= difference.damage;
-
- debug!(" > style difference: {:?}", difference);
-
- // We need to cascade the children in order to ensure the correct
- // propagation of inherited computed value flags.
- if old_values.flags.maybe_inherited() != new_values.flags.maybe_inherited() {
- debug!(
- " > flags changed: {:?} != {:?}",
- old_values.flags, new_values.flags
- );
- return ChildRestyleRequirement::MustCascadeChildren;
- }
-
- match difference.change {
- StyleChange::Unchanged => return ChildRestyleRequirement::CanSkipCascade,
- StyleChange::Changed { reset_only } => {
- // If inherited properties changed, the best we can do is
- // cascade the children.
- if !reset_only {
- return ChildRestyleRequirement::MustCascadeChildren;
- }
- },
- }
-
- let old_display = old_values.clone_display();
- let new_display = new_values.clone_display();
-
- if old_display != new_display {
- // If we used to be a display: none element, and no longer are, our
- // children need to be restyled because they're unstyled.
- if old_display == Display::None {
- return ChildRestyleRequirement::MustCascadeChildren;
- }
- // Blockification of children may depend on our display value,
- // so we need to actually do the recascade. We could potentially
- // do better, but it doesn't seem worth it.
- if old_display.is_item_container() != new_display.is_item_container() {
- return ChildRestyleRequirement::MustCascadeChildren;
- }
- // We may also need to blockify and un-blockify descendants if our
- // display goes from / to display: contents, since the "layout
- // parent style" changes.
- if old_display.is_contents() || new_display.is_contents() {
- return ChildRestyleRequirement::MustCascadeChildren;
- }
- // Line break suppression may also be affected if the display
- // type changes from ruby to non-ruby.
- #[cfg(feature = "gecko")]
- {
- if old_display.is_ruby_type() != new_display.is_ruby_type() {
- return ChildRestyleRequirement::MustCascadeChildren;
- }
- }
- }
-
- // Children with justify-items: auto may depend on our
- // justify-items property value.
- //
- // Similarly, we could potentially do better, but this really
- // seems not common enough to care about.
- #[cfg(feature = "gecko")]
- {
- use crate::values::specified::align::AlignFlags;
-
- let old_justify_items = old_values.get_position().clone_justify_items();
- let new_justify_items = new_values.get_position().clone_justify_items();
-
- let was_legacy_justify_items =
- old_justify_items.computed.0.contains(AlignFlags::LEGACY);
-
- let is_legacy_justify_items = new_justify_items.computed.0.contains(AlignFlags::LEGACY);
-
- if is_legacy_justify_items != was_legacy_justify_items {
- return ChildRestyleRequirement::MustCascadeChildren;
- }
-
- if was_legacy_justify_items && old_justify_items.computed != new_justify_items.computed
- {
- return ChildRestyleRequirement::MustCascadeChildren;
- }
- }
-
- #[cfg(feature = "servo")]
- {
- // We may need to set or propagate the CAN_BE_FRAGMENTED bit
- // on our children.
- if old_values.is_multicol() != new_values.is_multicol() {
- return ChildRestyleRequirement::MustCascadeChildren;
- }
- }
-
- // We could prove that, if our children don't inherit reset
- // properties, we can stop the cascade.
- ChildRestyleRequirement::MustCascadeChildrenIfInheritResetStyle
- }
-}
-
-impl<E: TElement> PrivateMatchMethods for E {}
-
-/// The public API that elements expose for selector matching.
-pub trait MatchMethods: TElement {
- /// Returns the closest parent element that doesn't have a display: contents
- /// style (and thus generates a box).
- ///
- /// This is needed to correctly handle blockification of flex and grid
- /// items.
- ///
- /// Returns itself if the element has no parent. In practice this doesn't
- /// happen because the root element is blockified per spec, but it could
- /// happen if we decide to not blockify for roots of disconnected subtrees,
- /// which is a kind of dubious behavior.
- fn layout_parent(&self) -> Self {
- let mut current = self.clone();
- loop {
- current = match current.traversal_parent() {
- Some(el) => el,
- None => return current,
- };
-
- let is_display_contents = current
- .borrow_data()
- .unwrap()
- .styles
- .primary()
- .is_display_contents();
-
- if !is_display_contents {
- return current;
- }
- }
- }
-
- /// Updates the styles with the new ones, diffs them, and stores the restyle
- /// damage.
- fn finish_restyle(
- &self,
- context: &mut StyleContext<Self>,
- data: &mut ElementData,
- mut new_styles: ResolvedElementStyles,
- important_rules_changed: bool,
- ) -> ChildRestyleRequirement {
- use std::cmp;
-
- self.process_animations(
- context,
- &mut data.styles,
- &mut new_styles,
- data.hint,
- important_rules_changed,
- );
-
- // First of all, update the styles.
- let old_styles = data.set_styles(new_styles);
-
- let new_primary_style = data.styles.primary.as_ref().unwrap();
-
- let mut restyle_requirement = ChildRestyleRequirement::CanSkipCascade;
- let is_root = new_primary_style
- .flags
- .contains(ComputedValueFlags::IS_ROOT_ELEMENT_STYLE);
- let is_container = !new_primary_style
- .get_box()
- .clone_container_type()
- .is_normal();
- if is_root || is_container {
- let new_font_size = new_primary_style.get_font().clone_font_size();
- let old_font_size = old_styles
- .primary
- .as_ref()
- .map(|s| s.get_font().clone_font_size());
-
- if old_font_size != Some(new_font_size) {
- if is_root {
- let device = context.shared.stylist.device();
- debug_assert!(self.owner_doc_matches_for_testing(device));
- device.set_root_font_size(new_font_size.computed_size().into());
- if device.used_root_font_size() {
- // If the root font-size changed since last time, and something
- // in the document did use rem units, ensure we recascade the
- // entire tree.
- restyle_requirement = ChildRestyleRequirement::MustCascadeDescendants;
- }
- }
-
- if is_container && old_font_size.is_some() {
- // TODO(emilio): Maybe only do this if we were matched
- // against relative font sizes?
- // Also, maybe we should do this as well for font-family /
- // etc changes (for ex/ch/ic units to work correctly)? We
- // should probably do the optimization mentioned above if
- // so.
- restyle_requirement = ChildRestyleRequirement::MustMatchDescendants;
- }
- }
- }
-
- if context.shared.stylist.quirks_mode() == QuirksMode::Quirks {
- if self.is_html_document_body_element() {
- // NOTE(emilio): We _could_ handle dynamic changes to it if it
- // changes and before we reach our children the cascade stops,
- // but we don't track right now whether we use the document body
- // color, and nobody else handles that properly anyway.
- let device = context.shared.stylist.device();
-
- // Needed for the "inherit from body" quirk.
- let text_color = new_primary_style.get_inherited_text().clone_color();
- device.set_body_text_color(text_color);
- }
- }
-
- // Don't accumulate damage if we're in the final animation traversal.
- if context
- .shared
- .traversal_flags
- .contains(TraversalFlags::FinalAnimationTraversal)
- {
- return ChildRestyleRequirement::MustCascadeChildren;
- }
-
- // Also, don't do anything if there was no style.
- let old_primary_style = match old_styles.primary {
- Some(s) => s,
- None => return ChildRestyleRequirement::MustCascadeChildren,
- };
-
- let old_container_type = old_primary_style.clone_container_type();
- let new_container_type = new_primary_style.clone_container_type();
- if old_container_type != new_container_type && !new_container_type.is_size_container_type()
- {
- // Stopped being a size container. Re-evaluate container queries and units on all our descendants.
- // Changes into and between different size containment is handled in `UpdateContainerQueryStyles`.
- restyle_requirement = ChildRestyleRequirement::MustMatchDescendants;
- } else if old_container_type.is_size_container_type() &&
- !old_primary_style.is_display_contents() &&
- new_primary_style.is_display_contents()
- {
- // Also re-evaluate when a container gets 'display: contents', since size queries will now evaluate to unknown.
- // Other displays like 'inline' will keep generating a box, so they are handled in `UpdateContainerQueryStyles`.
- restyle_requirement = ChildRestyleRequirement::MustMatchDescendants;
- }
-
- restyle_requirement = cmp::max(
- restyle_requirement,
- self.accumulate_damage_for(
- context.shared,
- &mut data.damage,
- &old_primary_style,
- new_primary_style,
- None,
- ),
- );
-
- if data.styles.pseudos.is_empty() && old_styles.pseudos.is_empty() {
- // This is the common case; no need to examine pseudos here.
- return restyle_requirement;
- }
-
- let pseudo_styles = old_styles
- .pseudos
- .as_array()
- .iter()
- .zip(data.styles.pseudos.as_array().iter());
-
- for (i, (old, new)) in pseudo_styles.enumerate() {
- match (old, new) {
- (&Some(ref old), &Some(ref new)) => {
- self.accumulate_damage_for(
- context.shared,
- &mut data.damage,
- old,
- new,
- Some(&PseudoElement::from_eager_index(i)),
- );
- },
- (&None, &None) => {},
- _ => {
- // It's possible that we're switching from not having
- // ::before/::after at all to having styles for them but not
- // actually having a useful pseudo-element. Check for that
- // case.
- let pseudo = PseudoElement::from_eager_index(i);
- let new_pseudo_should_exist =
- new.as_ref().map_or(false, |s| pseudo.should_exist(s));
- let old_pseudo_should_exist =
- old.as_ref().map_or(false, |s| pseudo.should_exist(s));
- if new_pseudo_should_exist != old_pseudo_should_exist {
- data.damage |= RestyleDamage::reconstruct();
- return restyle_requirement;
- }
- },
- }
- }
-
- restyle_requirement
- }
-
- /// Updates the rule nodes without re-running selector matching, using just
- /// the rule tree.
- ///
- /// Returns true if an !important rule was replaced.
- fn replace_rules(
- &self,
- replacements: RestyleHint,
- context: &mut StyleContext<Self>,
- cascade_inputs: &mut ElementCascadeInputs,
- ) -> bool {
- let mut result = false;
- result |= self.replace_rules_internal(
- replacements,
- context,
- CascadeVisitedMode::Unvisited,
- cascade_inputs,
- );
- result |= self.replace_rules_internal(
- replacements,
- context,
- CascadeVisitedMode::Visited,
- cascade_inputs,
- );
- result
- }
-
- /// Given the old and new style of this element, and whether it's a
- /// pseudo-element, compute the restyle damage used to determine which
- /// kind of layout or painting operations we'll need.
- fn compute_style_difference(
- &self,
- old_values: &ComputedValues,
- new_values: &ComputedValues,
- pseudo: Option<&PseudoElement>,
- ) -> StyleDifference {
- debug_assert!(pseudo.map_or(true, |p| p.is_eager()));
- RestyleDamage::compute_style_difference(old_values, new_values)
- }
-}
-
-impl<E: TElement> MatchMethods for E {}
diff --git a/components/style/media_queries/media_list.rs b/components/style/media_queries/media_list.rs
deleted file mode 100644
index 3c2ba9ee5c1..00000000000
--- a/components/style/media_queries/media_list.rs
+++ /dev/null
@@ -1,150 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
-
-//! A media query list:
-//!
-//! https://drafts.csswg.org/mediaqueries/#typedef-media-query-list
-
-use super::{Device, MediaQuery, Qualifier};
-use crate::context::QuirksMode;
-use crate::error_reporting::ContextualParseError;
-use crate::parser::ParserContext;
-use crate::queries::condition::KleeneValue;
-use crate::values::computed;
-use cssparser::{Delimiter, Parser};
-use cssparser::{ParserInput, Token};
-
-/// A type that encapsulates a media query list.
-#[derive(Clone, MallocSizeOf, ToCss, ToShmem)]
-#[css(comma, derive_debug)]
-pub struct MediaList {
- /// The list of media queries.
- #[css(iterable)]
- pub media_queries: Vec<MediaQuery>,
-}
-
-impl MediaList {
- /// Parse a media query list from CSS.
- ///
- /// Always returns a media query list. If any invalid media query is
- /// found, the media query list is only filled with the equivalent of
- /// "not all", see:
- ///
- /// <https://drafts.csswg.org/mediaqueries/#error-handling>
- pub fn parse(context: &ParserContext, input: &mut Parser) -> Self {
- if input.is_exhausted() {
- return Self::empty();
- }
-
- let mut media_queries = vec![];
- loop {
- let start_position = input.position();
- match input.parse_until_before(Delimiter::Comma, |i| MediaQuery::parse(context, i)) {
- Ok(mq) => {
- media_queries.push(mq);
- },
- Err(err) => {
- media_queries.push(MediaQuery::never_matching());
- let location = err.location;
- let error = ContextualParseError::InvalidMediaRule(
- input.slice_from(start_position),
- err,
- );
- context.log_css_error(location, error);
- },
- }
-
- match input.next() {
- Ok(&Token::Comma) => {},
- Ok(_) => unreachable!(),
- Err(_) => break,
- }
- }
-
- MediaList { media_queries }
- }
-
- /// Create an empty MediaList.
- pub fn empty() -> Self {
- MediaList {
- media_queries: vec![],
- }
- }
-
- /// Evaluate a whole `MediaList` against `Device`.
- pub fn evaluate(&self, device: &Device, quirks_mode: QuirksMode) -> bool {
- // Check if it is an empty media query list or any queries match.
- // https://drafts.csswg.org/mediaqueries-4/#mq-list
- if self.media_queries.is_empty() {
- return true;
- }
-
- computed::Context::for_media_query_evaluation(device, quirks_mode, |context| {
- self.media_queries.iter().any(|mq| {
- let mut query_match = if mq.media_type.matches(device.media_type()) {
- mq.condition
- .as_ref()
- .map_or(KleeneValue::True, |c| c.matches(context))
- } else {
- KleeneValue::False
- };
-
- // Apply the logical NOT qualifier to the result
- if matches!(mq.qualifier, Some(Qualifier::Not)) {
- query_match = !query_match;
- }
- query_match.to_bool(/* unknown = */ false)
- })
- })
- }
-
- /// Whether this `MediaList` contains no media queries.
- pub fn is_empty(&self) -> bool {
- self.media_queries.is_empty()
- }
-
- /// Whether this `MediaList` depends on the viewport size.
- pub fn is_viewport_dependent(&self) -> bool {
- self.media_queries.iter().any(|q| q.is_viewport_dependent())
- }
-
- /// Append a new media query item to the media list.
- /// <https://drafts.csswg.org/cssom/#dom-medialist-appendmedium>
- ///
- /// Returns true if added, false if fail to parse the medium string.
- pub fn append_medium(&mut self, context: &ParserContext, new_medium: &str) -> bool {
- let mut input = ParserInput::new(new_medium);
- let mut parser = Parser::new(&mut input);
- let new_query = match MediaQuery::parse(&context, &mut parser) {
- Ok(query) => query,
- Err(_) => {
- return false;
- },
- };
- // This algorithm doesn't actually matches the current spec,
- // but it matches the behavior of Gecko and Edge.
- // See https://github.com/w3c/csswg-drafts/issues/697
- self.media_queries.retain(|query| query != &new_query);
- self.media_queries.push(new_query);
- true
- }
-
- /// Delete a media query from the media list.
- /// <https://drafts.csswg.org/cssom/#dom-medialist-deletemedium>
- ///
- /// Returns true if found and deleted, false otherwise.
- pub fn delete_medium(&mut self, context: &ParserContext, old_medium: &str) -> bool {
- let mut input = ParserInput::new(old_medium);
- let mut parser = Parser::new(&mut input);
- let old_query = match MediaQuery::parse(context, &mut parser) {
- Ok(query) => query,
- Err(_) => {
- return false;
- },
- };
- let old_len = self.media_queries.len();
- self.media_queries.retain(|query| query != &old_query);
- old_len != self.media_queries.len()
- }
-}
diff --git a/components/style/media_queries/media_query.rs b/components/style/media_queries/media_query.rs
deleted file mode 100644
index c30a4453930..00000000000
--- a/components/style/media_queries/media_query.rs
+++ /dev/null
@@ -1,193 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
-
-//! A media query:
-//!
-//! https://drafts.csswg.org/mediaqueries/#typedef-media-query
-
-use crate::parser::ParserContext;
-use crate::queries::{FeatureFlags, FeatureType, QueryCondition};
-use crate::str::string_as_ascii_lowercase;
-use crate::values::CustomIdent;
-use crate::Atom;
-use cssparser::Parser;
-use std::fmt::{self, Write};
-use style_traits::{CssWriter, ParseError, ToCss};
-
-/// <https://drafts.csswg.org/mediaqueries/#mq-prefix>
-#[derive(Clone, Copy, Debug, Eq, MallocSizeOf, Parse, PartialEq, ToCss, ToShmem)]
-pub enum Qualifier {
- /// Hide a media query from legacy UAs:
- /// <https://drafts.csswg.org/mediaqueries/#mq-only>
- Only,
- /// Negate a media query:
- /// <https://drafts.csswg.org/mediaqueries/#mq-not>
- Not,
-}
-
-/// <https://drafts.csswg.org/mediaqueries/#media-types>
-#[derive(Clone, Debug, Eq, MallocSizeOf, PartialEq, ToShmem)]
-pub struct MediaType(pub CustomIdent);
-
-impl MediaType {
- /// The `screen` media type.
- pub fn screen() -> Self {
- MediaType(CustomIdent(atom!("screen")))
- }
-
- /// The `print` media type.
- pub fn print() -> Self {
- MediaType(CustomIdent(atom!("print")))
- }
-
- fn parse(name: &str) -> Result<Self, ()> {
- // From https://drafts.csswg.org/mediaqueries/#mq-syntax:
- //
- // The <media-type> production does not include the keywords only, not, and, or, and layer.
- //
- // Here we also perform the to-ascii-lowercase part of the serialization
- // algorithm: https://drafts.csswg.org/cssom/#serializing-media-queries
- match_ignore_ascii_case! { name,
- "not" | "or" | "and" | "only" | "layer" => Err(()),
- _ => Ok(MediaType(CustomIdent(Atom::from(string_as_ascii_lowercase(name))))),
- }
- }
-}
-
-/// A [media query][mq].
-///
-/// [mq]: https://drafts.csswg.org/mediaqueries/
-#[derive(Clone, Debug, MallocSizeOf, PartialEq, ToShmem)]
-pub struct MediaQuery {
- /// The qualifier for this query.
- pub qualifier: Option<Qualifier>,
- /// The media type for this query, that can be known, unknown, or "all".
- pub media_type: MediaQueryType,
- /// The condition that this media query contains. This cannot have `or`
- /// in the first level.
- pub condition: Option<QueryCondition>,
-}
-
-impl ToCss for MediaQuery {
- fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
- where
- W: Write,
- {
- if let Some(qual) = self.qualifier {
- qual.to_css(dest)?;
- dest.write_char(' ')?;
- }
-
- match self.media_type {
- MediaQueryType::All => {
- // We need to print "all" if there's a qualifier, or there's
- // just an empty list of expressions.
- //
- // Otherwise, we'd serialize media queries like "(min-width:
- // 40px)" in "all (min-width: 40px)", which is unexpected.
- if self.qualifier.is_some() || self.condition.is_none() {
- dest.write_str("all")?;
- }
- },
- MediaQueryType::Concrete(MediaType(ref desc)) => desc.to_css(dest)?,
- }
-
- let condition = match self.condition {
- Some(ref c) => c,
- None => return Ok(()),
- };
-
- if self.media_type != MediaQueryType::All || self.qualifier.is_some() {
- dest.write_str(" and ")?;
- }
-
- condition.to_css(dest)
- }
-}
-
-impl MediaQuery {
- /// Return a media query that never matches, used for when we fail to parse
- /// a given media query.
- pub fn never_matching() -> Self {
- Self {
- qualifier: Some(Qualifier::Not),
- media_type: MediaQueryType::All,
- condition: None,
- }
- }
-
- /// Returns whether this media query depends on the viewport.
- pub fn is_viewport_dependent(&self) -> bool {
- self.condition.as_ref().map_or(false, |c| {
- return c
- .cumulative_flags()
- .contains(FeatureFlags::VIEWPORT_DEPENDENT);
- })
- }
-
- /// Parse a media query given css input.
- ///
- /// Returns an error if any of the expressions is unknown.
- pub fn parse<'i, 't>(
- context: &ParserContext,
- input: &mut Parser<'i, 't>,
- ) -> Result<Self, ParseError<'i>> {
- let (qualifier, explicit_media_type) = input
- .try_parse(|input| -> Result<_, ()> {
- let qualifier = input.try_parse(Qualifier::parse).ok();
- let ident = input.expect_ident().map_err(|_| ())?;
- let media_type = MediaQueryType::parse(&ident)?;
- Ok((qualifier, Some(media_type)))
- })
- .unwrap_or_default();
-
- let condition = if explicit_media_type.is_none() {
- Some(QueryCondition::parse(context, input, FeatureType::Media)?)
- } else if input.try_parse(|i| i.expect_ident_matching("and")).is_ok() {
- Some(QueryCondition::parse_disallow_or(
- context,
- input,
- FeatureType::Media,
- )?)
- } else {
- None
- };
-
- let media_type = explicit_media_type.unwrap_or(MediaQueryType::All);
- Ok(Self {
- qualifier,
- media_type,
- condition,
- })
- }
-}
-
-/// <http://dev.w3.org/csswg/mediaqueries-3/#media0>
-#[derive(Clone, Debug, Eq, MallocSizeOf, PartialEq, ToShmem)]
-pub enum MediaQueryType {
- /// A media type that matches every device.
- All,
- /// A specific media type.
- Concrete(MediaType),
-}
-
-impl MediaQueryType {
- fn parse(ident: &str) -> Result<Self, ()> {
- match_ignore_ascii_case! { ident,
- "all" => return Ok(MediaQueryType::All),
- _ => (),
- };
-
- // If parseable, accept this type as a concrete type.
- MediaType::parse(ident).map(MediaQueryType::Concrete)
- }
-
- /// Returns whether this media query type matches a MediaType.
- pub fn matches(&self, other: MediaType) -> bool {
- match *self {
- MediaQueryType::All => true,
- MediaQueryType::Concrete(ref known_type) => *known_type == other,
- }
- }
-}
diff --git a/components/style/media_queries/mod.rs b/components/style/media_queries/mod.rs
deleted file mode 100644
index 833f6f53cb9..00000000000
--- a/components/style/media_queries/mod.rs
+++ /dev/null
@@ -1,18 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
-
-//! [Media queries][mq].
-//!
-//! [mq]: https://drafts.csswg.org/mediaqueries/
-
-mod media_list;
-mod media_query;
-
-pub use self::media_list::MediaList;
-pub use self::media_query::{MediaQuery, MediaQueryType, MediaType, Qualifier};
-
-#[cfg(feature = "gecko")]
-pub use crate::gecko::media_queries::Device;
-#[cfg(feature = "servo")]
-pub use crate::servo::media_queries::Device;
diff --git a/components/style/parallel.rs b/components/style/parallel.rs
deleted file mode 100644
index 2d0e0c7ffcc..00000000000
--- a/components/style/parallel.rs
+++ /dev/null
@@ -1,197 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
-
-//! Implements parallel traversal over the DOM tree.
-//!
-//! This traversal is based on Rayon, and therefore its safety is largely
-//! verified by the type system.
-//!
-//! The primary trickiness and fine print for the above relates to the
-//! thread safety of the DOM nodes themselves. Accessing a DOM element
-//! concurrently on multiple threads is actually mostly "safe", since all
-//! the mutable state is protected by an AtomicRefCell, and so we'll
-//! generally panic if something goes wrong. Still, we try to to enforce our
-//! thread invariants at compile time whenever possible. As such, TNode and
-//! TElement are not Send, so ordinary style system code cannot accidentally
-//! share them with other threads. In the parallel traversal, we explicitly
-//! invoke |unsafe { SendNode::new(n) }| to put nodes in containers that may
-//! be sent to other threads. This occurs in only a handful of places and is
-//! easy to grep for. At the time of this writing, there is no other unsafe
-//! code in the parallel traversal.
-
-#![deny(missing_docs)]
-
-use crate::context::{StyleContext, ThreadLocalStyleContext};
-use crate::dom::{OpaqueNode, SendNode, TElement};
-use crate::scoped_tls::ScopedTLS;
-use crate::traversal::{DomTraversal, PerLevelTraversalData};
-use rayon;
-use std::collections::VecDeque;
-
-/// The minimum stack size for a thread in the styling pool, in kilobytes.
-#[cfg(feature = "gecko")]
-pub const STYLE_THREAD_STACK_SIZE_KB: usize = 256;
-
-/// The minimum stack size for a thread in the styling pool, in kilobytes.
-/// Servo requires a bigger stack in debug builds.
-#[cfg(feature = "servo")]
-pub const STYLE_THREAD_STACK_SIZE_KB: usize = 512;
-
-/// The stack margin. If we get this deep in the stack, we will skip recursive
-/// optimizations to ensure that there is sufficient room for non-recursive work.
-///
-/// We allocate large safety margins because certain OS calls can use very large
-/// amounts of stack space [1]. Reserving a larger-than-necessary stack costs us
-/// address space, but if we keep our safety margin big, we will generally avoid
-/// committing those extra pages, and only use them in edge cases that would
-/// otherwise cause crashes.
-///
-/// When measured with 128KB stacks and 40KB margin, we could support 53
-/// levels of recursion before the limiter kicks in, on x86_64-Linux [2]. When
-/// we doubled the stack size, we added it all to the safety margin, so we should
-/// be able to get the same amount of recursion.
-///
-/// [1] https://bugzilla.mozilla.org/show_bug.cgi?id=1395708#c15
-/// [2] See Gecko bug 1376883 for more discussion on the measurements.
-pub const STACK_SAFETY_MARGIN_KB: usize = 168;
-
-/// A callback to create our thread local context. This needs to be
-/// out of line so we don't allocate stack space for the entire struct
-/// in the caller.
-#[inline(never)]
-fn create_thread_local_context<'scope, E>(slot: &mut Option<ThreadLocalStyleContext<E>>)
-where
- E: TElement + 'scope,
-{
- *slot = Some(ThreadLocalStyleContext::new());
-}
-
-// Sends one chunk of work to the thread-pool.
-fn distribute_one_chunk<'a, 'scope, E, D>(
- items: VecDeque<SendNode<E::ConcreteNode>>,
- traversal_root: OpaqueNode,
- work_unit_max: usize,
- traversal_data: PerLevelTraversalData,
- scope: &'a rayon::ScopeFifo<'scope>,
- traversal: &'scope D,
- tls: &'scope ScopedTLS<'scope, ThreadLocalStyleContext<E>>,
-) where
- E: TElement + 'scope,
- D: DomTraversal<E>,
-{
- scope.spawn_fifo(move |scope| {
- #[cfg(feature = "gecko")]
- gecko_profiler_label!(Layout, StyleComputation);
- let mut tlc = tls.ensure(create_thread_local_context);
- let mut context = StyleContext {
- shared: traversal.shared_context(),
- thread_local: &mut *tlc,
- };
- style_trees(
- &mut context,
- items,
- traversal_root,
- work_unit_max,
- static_prefs::pref!("layout.css.stylo-local-work-queue.in-worker") as usize,
- traversal_data,
- Some(scope),
- traversal,
- Some(tls),
- );
- })
-}
-
-/// Distributes all items into the thread pool, in `work_unit_max` chunks.
-fn distribute_work<'a, 'scope, E, D>(
- mut items: VecDeque<SendNode<E::ConcreteNode>>,
- traversal_root: OpaqueNode,
- work_unit_max: usize,
- traversal_data: PerLevelTraversalData,
- scope: &'a rayon::ScopeFifo<'scope>,
- traversal: &'scope D,
- tls: &'scope ScopedTLS<'scope, ThreadLocalStyleContext<E>>,
-) where
- E: TElement + 'scope,
- D: DomTraversal<E>,
-{
- while items.len() > work_unit_max {
- let rest = items.split_off(work_unit_max);
- distribute_one_chunk(
- items,
- traversal_root,
- work_unit_max,
- traversal_data,
- scope,
- traversal,
- tls,
- );
- items = rest;
- }
- distribute_one_chunk(
- items,
- traversal_root,
- work_unit_max,
- traversal_data,
- scope,
- traversal,
- tls,
- );
-}
-
-/// Processes `discovered` items, possibly spawning work in other threads as needed.
-#[inline]
-pub fn style_trees<'a, 'scope, E, D>(
- context: &mut StyleContext<E>,
- mut discovered: VecDeque<SendNode<E::ConcreteNode>>,
- traversal_root: OpaqueNode,
- work_unit_max: usize,
- local_queue_size: usize,
- mut traversal_data: PerLevelTraversalData,
- scope: Option<&'a rayon::ScopeFifo<'scope>>,
- traversal: &'scope D,
- tls: Option<&'scope ScopedTLS<'scope, ThreadLocalStyleContext<E>>>,
-) where
- E: TElement + 'scope,
- D: DomTraversal<E>,
-{
- let mut nodes_remaining_at_current_depth = discovered.len();
- while let Some(node) = discovered.pop_front() {
- let mut children_to_process = 0isize;
- traversal.process_preorder(&traversal_data, context, *node, |n| {
- children_to_process += 1;
- discovered.push_back(unsafe { SendNode::new(n) });
- });
-
- traversal.handle_postorder_traversal(context, traversal_root, *node, children_to_process);
-
- nodes_remaining_at_current_depth -= 1;
-
- // If we have enough children at the next depth in the DOM, spawn them to a different job
- // relatively soon, while keeping always at least `local_queue_size` worth of work for
- // ourselves.
- let discovered_children = discovered.len() - nodes_remaining_at_current_depth;
- if discovered_children >= work_unit_max &&
- discovered.len() >= local_queue_size + work_unit_max &&
- scope.is_some()
- {
- let kept_work = std::cmp::max(nodes_remaining_at_current_depth, local_queue_size);
- let mut traversal_data_copy = traversal_data.clone();
- traversal_data_copy.current_dom_depth += 1;
- distribute_work(
- discovered.split_off(kept_work),
- traversal_root,
- work_unit_max,
- traversal_data_copy,
- scope.unwrap(),
- traversal,
- tls.unwrap(),
- );
- }
-
- if nodes_remaining_at_current_depth == 0 {
- traversal_data.current_dom_depth += 1;
- nodes_remaining_at_current_depth = discovered.len();
- }
- }
-}
diff --git a/components/style/parser.rs b/components/style/parser.rs
deleted file mode 100644
index 8d8d408f53f..00000000000
--- a/components/style/parser.rs
+++ /dev/null
@@ -1,210 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
-
-//! The context within which CSS code is parsed.
-
-use crate::context::QuirksMode;
-use crate::error_reporting::{ContextualParseError, ParseErrorReporter};
-use crate::stylesheets::{CssRuleType, CssRuleTypes, Namespaces, Origin, UrlExtraData};
-use crate::use_counters::UseCounters;
-use cssparser::{Parser, SourceLocation, UnicodeRange};
-use std::borrow::Cow;
-use style_traits::{OneOrMoreSeparated, ParseError, ParsingMode, Separator};
-
-/// Asserts that all ParsingMode flags have a matching ParsingMode value in gecko.
-#[cfg(feature = "gecko")]
-#[inline]
-pub fn assert_parsing_mode_match() {
- use crate::gecko_bindings::structs;
-
- macro_rules! check_parsing_modes {
- ( $( $a:ident => $b:path ),*, ) => {
- if cfg!(debug_assertions) {
- let mut modes = ParsingMode::all();
- $(
- assert_eq!(structs::$a as usize, $b.bits() as usize, stringify!($b));
- modes.remove($b);
- )*
- assert_eq!(modes, ParsingMode::empty(), "all ParsingMode bits should have an assertion");
- }
- }
- }
-
- check_parsing_modes! {
- ParsingMode_Default => ParsingMode::DEFAULT,
- ParsingMode_AllowUnitlessLength => ParsingMode::ALLOW_UNITLESS_LENGTH,
- ParsingMode_AllowAllNumericValues => ParsingMode::ALLOW_ALL_NUMERIC_VALUES,
- }
-}
-
-/// The data that the parser needs from outside in order to parse a stylesheet.
-pub struct ParserContext<'a> {
- /// The `Origin` of the stylesheet, whether it's a user, author or
- /// user-agent stylesheet.
- pub stylesheet_origin: Origin,
- /// The extra data we need for resolving url values.
- pub url_data: &'a UrlExtraData,
- /// The current rule types, if any.
- pub rule_types: CssRuleTypes,
- /// The mode to use when parsing.
- pub parsing_mode: ParsingMode,
- /// The quirks mode of this stylesheet.
- pub quirks_mode: QuirksMode,
- /// The active error reporter, or none if error reporting is disabled.
- error_reporter: Option<&'a dyn ParseErrorReporter>,
- /// The currently active namespaces.
- pub namespaces: Cow<'a, Namespaces>,
- /// The use counters we want to record while parsing style rules, if any.
- pub use_counters: Option<&'a UseCounters>,
-}
-
-impl<'a> ParserContext<'a> {
- /// Create a parser context.
- #[inline]
- pub fn new(
- stylesheet_origin: Origin,
- url_data: &'a UrlExtraData,
- rule_type: Option<CssRuleType>,
- parsing_mode: ParsingMode,
- quirks_mode: QuirksMode,
- namespaces: Cow<'a, Namespaces>,
- error_reporter: Option<&'a dyn ParseErrorReporter>,
- use_counters: Option<&'a UseCounters>,
- ) -> Self {
- Self {
- stylesheet_origin,
- url_data,
- rule_types: rule_type.map(CssRuleTypes::from).unwrap_or_default(),
- parsing_mode,
- quirks_mode,
- error_reporter,
- namespaces,
- use_counters,
- }
- }
-
- /// Temporarily sets the rule_type and executes the callback function, returning its result.
- pub fn nest_for_rule<R>(
- &mut self,
- rule_type: CssRuleType,
- cb: impl FnOnce(&mut Self) -> R,
- ) -> R {
- let old_rule_types = self.rule_types;
- self.rule_types.insert(rule_type);
- let r = cb(self);
- self.rule_types = old_rule_types;
- r
- }
-
- /// Whether we're in a @page rule.
- #[inline]
- pub fn in_page_rule(&self) -> bool {
- self.rule_types.contains(CssRuleType::Page)
- }
-
- /// Get the rule type, which assumes that one is available.
- pub fn rule_types(&self) -> CssRuleTypes {
- self.rule_types
- }
-
- /// Returns whether CSS error reporting is enabled.
- #[inline]
- pub fn error_reporting_enabled(&self) -> bool {
- self.error_reporter.is_some()
- }
-
- /// Record a CSS parse error with this context’s error reporting.
- pub fn log_css_error(&self, location: SourceLocation, error: ContextualParseError) {
- let error_reporter = match self.error_reporter {
- Some(r) => r,
- None => return,
- };
-
- error_reporter.report_error(self.url_data, location, error)
- }
-
- /// Whether we're in a user-agent stylesheet.
- #[inline]
- pub fn in_ua_sheet(&self) -> bool {
- self.stylesheet_origin == Origin::UserAgent
- }
-
- /// Returns whether chrome-only rules should be parsed.
- #[inline]
- pub fn chrome_rules_enabled(&self) -> bool {
- self.url_data.chrome_rules_enabled() || self.stylesheet_origin == Origin::User
- }
-
- /// Whether we're in a user-agent stylesheet or chrome rules are enabled.
- #[inline]
- pub fn in_ua_or_chrome_sheet(&self) -> bool {
- self.in_ua_sheet() || self.chrome_rules_enabled()
- }
-}
-
-/// A trait to abstract parsing of a specified value given a `ParserContext` and
-/// CSS input.
-///
-/// This can be derived on keywords with `#[derive(Parse)]`.
-///
-/// The derive code understands the following attributes on each of the variants:
-///
-/// * `#[parse(aliases = "foo,bar")]` can be used to alias a value with another
-/// at parse-time.
-///
-/// * `#[parse(condition = "function")]` can be used to make the parsing of the
-/// value conditional on `function`, which needs to fulfill
-/// `fn(&ParserContext) -> bool`.
-pub trait Parse: Sized {
- /// Parse a value of this type.
- ///
- /// Returns an error on failure.
- fn parse<'i, 't>(
- context: &ParserContext,
- input: &mut Parser<'i, 't>,
- ) -> Result<Self, ParseError<'i>>;
-}
-
-impl<T> Parse for Vec<T>
-where
- T: Parse + OneOrMoreSeparated,
- <T as OneOrMoreSeparated>::S: Separator,
-{
- fn parse<'i, 't>(
- context: &ParserContext,
- input: &mut Parser<'i, 't>,
- ) -> Result<Self, ParseError<'i>> {
- <T as OneOrMoreSeparated>::S::parse(input, |i| T::parse(context, i))
- }
-}
-
-impl<T> Parse for Box<T>
-where
- T: Parse,
-{
- fn parse<'i, 't>(
- context: &ParserContext,
- input: &mut Parser<'i, 't>,
- ) -> Result<Self, ParseError<'i>> {
- T::parse(context, input).map(Box::new)
- }
-}
-
-impl Parse for crate::OwnedStr {
- fn parse<'i, 't>(
- _: &ParserContext,
- input: &mut Parser<'i, 't>,
- ) -> Result<Self, ParseError<'i>> {
- Ok(input.expect_string()?.as_ref().to_owned().into())
- }
-}
-
-impl Parse for UnicodeRange {
- fn parse<'i, 't>(
- _: &ParserContext,
- input: &mut Parser<'i, 't>,
- ) -> Result<Self, ParseError<'i>> {
- Ok(UnicodeRange::parse(input)?)
- }
-}
diff --git a/components/style/piecewise_linear.rs b/components/style/piecewise_linear.rs
deleted file mode 100644
index 84ccb7061c3..00000000000
--- a/components/style/piecewise_linear.rs
+++ /dev/null
@@ -1,293 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
-
-//! A piecewise linear function, following CSS linear easing
-use crate::values::computed::Percentage;
-use core::slice::Iter;
-/// draft as in https://github.com/w3c/csswg-drafts/pull/6533.
-use euclid::approxeq::ApproxEq;
-use itertools::Itertools;
-use std::fmt::{self, Write};
-use style_traits::{CssWriter, ToCss};
-
-use crate::values::CSSFloat;
-
-type ValueType = CSSFloat;
-/// a single entry in a piecewise linear function.
-#[allow(missing_docs)]
-#[derive(
- Clone,
- Copy,
- Debug,
- MallocSizeOf,
- PartialEq,
- SpecifiedValueInfo,
- ToResolvedValue,
- Serialize,
- Deserialize,
-)]
-#[repr(C)]
-pub struct PiecewiseLinearFunctionEntry {
- pub x: ValueType,
- pub y: ValueType,
-}
-
-impl ToCss for PiecewiseLinearFunctionEntry {
- fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
- where
- W: fmt::Write,
- {
- self.y.to_css(dest)?;
- dest.write_char(' ')?;
- Percentage(self.x).to_css(dest)
- }
-}
-
-/// Representation of a piecewise linear function, a series of linear functions.
-#[derive(
- Default,
- Clone,
- Debug,
- MallocSizeOf,
- PartialEq,
- SpecifiedValueInfo,
- ToResolvedValue,
- ToCss,
- Serialize,
- Deserialize,
-)]
-#[repr(C)]
-#[css(comma)]
-pub struct PiecewiseLinearFunction {
- #[css(iterable)]
- entries: crate::OwnedSlice<PiecewiseLinearFunctionEntry>,
-}
-
-/// Parameters to define one linear stop.
-pub type PiecewiseLinearFunctionBuildParameters = (CSSFloat, Option<CSSFloat>);
-
-impl PiecewiseLinearFunction {
- /// Interpolate y value given x and two points. The linear function will be rooted at the asymptote.
- fn interpolate(
- x: ValueType,
- prev: PiecewiseLinearFunctionEntry,
- next: PiecewiseLinearFunctionEntry,
- asymptote: &PiecewiseLinearFunctionEntry,
- ) -> ValueType {
- // Short circuit if the x is on prev or next.
- // `next` point is preferred as per spec.
- if x.approx_eq(&next.x) {
- return next.y;
- }
- if x.approx_eq(&prev.x) {
- return prev.y;
- }
- // Avoid division by zero.
- if prev.x.approx_eq(&next.x) {
- return next.y;
- }
- let slope = (next.y - prev.y) / (next.x - prev.x);
- return slope * (x - asymptote.x) + asymptote.y;
- }
-
- /// Get the y value of the piecewise linear function given the x value, as per
- /// https://drafts.csswg.org/css-easing-2/#linear-easing-function-output
- pub fn at(&self, x: ValueType) -> ValueType {
- if !x.is_finite() {
- return if x > 0.0 { 1.0 } else { 0.0 };
- }
- if self.entries.is_empty() {
- // Implied y = x, as per spec.
- return x;
- }
- if self.entries.len() == 1 {
- // Implied y = <constant>, as per spec.
- return self.entries[0].y;
- }
- // Spec dictates the valid input domain is [0, 1]. Outside of this range, the output
- // should be calculated as if the slopes at start and end extend to infinity. However, if the
- // start/end have two points of the same position, the line should extend along the x-axis.
- // The function doesn't have to cover the input domain, in which case the extension logic
- // applies even if the input falls in the input domain.
- // Also, we're guaranteed to have at least two elements at this point.
- if x < self.entries[0].x {
- return Self::interpolate(x, self.entries[0], self.entries[1], &self.entries[0]);
- }
- let mut rev_iter = self.entries.iter().rev();
- let last = rev_iter.next().unwrap();
- if x >= last.x {
- let second_last = rev_iter.next().unwrap();
- return Self::interpolate(x, *second_last, *last, last);
- }
-
- // Now we know the input sits within the domain explicitly defined by our function.
- for (point_b, point_a) in self.entries.iter().rev().tuple_windows() {
- // Need to let point A be the _last_ point where its x is less than the input x,
- // hence the reverse traversal.
- if x < point_a.x {
- continue;
- }
- return Self::interpolate(x, *point_a, *point_b, point_a);
- }
- unreachable!("Input is supposed to be within the entries' min & max!");
- }
-
- /// Create the piecewise linear function from an iterator that generates the parameter tuple.
- pub fn from_iter<Iter>(iter: Iter) -> Self
- where
- Iter: Iterator<Item = PiecewiseLinearFunctionBuildParameters> + ExactSizeIterator,
- {
- let mut builder = PiecewiseLinearFunctionBuilder::with_capacity(iter.len());
- for (y, x_start) in iter {
- builder = builder.push(y, x_start);
- }
- builder.build()
- }
-
- #[allow(missing_docs)]
- pub fn iter(&self) -> Iter<PiecewiseLinearFunctionEntry> {
- self.entries.iter()
- }
-}
-
-/// Entry of a piecewise linear function while building, where the calculation of x value can be deferred.
-#[derive(Clone, Copy)]
-struct BuildEntry {
- x: Option<ValueType>,
- y: ValueType,
-}
-
-/// Builder object to generate a linear function.
-#[derive(Default)]
-pub struct PiecewiseLinearFunctionBuilder {
- largest_x: Option<ValueType>,
- smallest_x: Option<ValueType>,
- entries: Vec<BuildEntry>,
-}
-
-impl PiecewiseLinearFunctionBuilder {
- #[allow(missing_docs)]
- pub fn new() -> Self {
- PiecewiseLinearFunctionBuilder::default()
- }
-
- /// Create a builder for a known amount of linear stop entries.
- pub fn with_capacity(len: usize) -> Self {
- PiecewiseLinearFunctionBuilder {
- largest_x: None,
- smallest_x: None,
- entries: Vec::with_capacity(len),
- }
- }
-
- fn create_entry(&mut self, y: ValueType, x: Option<ValueType>) {
- let x = match x {
- Some(x) if x.is_finite() => x,
- _ if self.entries.is_empty() => 0.0, // First x is 0 if not specified (Or not finite)
- _ => {
- self.entries.push(BuildEntry { x: None, y });
- return;
- },
- };
- // Specified x value cannot regress, as per spec.
- let x = match self.largest_x {
- Some(largest_x) => x.max(largest_x),
- None => x,
- };
- self.largest_x = Some(x);
- // Whatever we see the earliest is the smallest value.
- if self.smallest_x.is_none() {
- self.smallest_x = Some(x);
- }
- self.entries.push(BuildEntry { x: Some(x), y });
- }
-
- /// Add a new entry into the piecewise linear function with specified y value.
- /// If the start x value is given, that is where the x value will be. Otherwise,
- /// the x value is calculated later. If the end x value is specified, a flat segment
- /// is generated. If start x value is not specified but end x is, it is treated as
- /// start x.
- pub fn push(mut self, y: CSSFloat, x_start: Option<CSSFloat>) -> Self {
- self.create_entry(y, x_start);
- self
- }
-
- /// Finish building the piecewise linear function by resolving all undefined x values,
- /// then return the result.
- pub fn build(mut self) -> PiecewiseLinearFunction {
- if self.entries.is_empty() {
- return PiecewiseLinearFunction::default();
- }
- if self.entries.len() == 1 {
- // Don't bother resolving anything.
- return PiecewiseLinearFunction {
- entries: crate::OwnedSlice::from_slice(&[PiecewiseLinearFunctionEntry {
- x: 0.,
- y: self.entries[0].y,
- }]),
- };
- }
- // Guaranteed at least two elements.
- // Start element's x value should've been assigned when the first value was pushed.
- debug_assert!(
- self.entries[0].x.is_some(),
- "Expected an entry with x defined!"
- );
- // Spec asserts that if the last entry does not have an x value, it is assigned the largest seen x value.
- self.entries
- .last_mut()
- .unwrap()
- .x
- .get_or_insert(self.largest_x.filter(|x| x > &1.0).unwrap_or(1.0));
- // Now we have at least two elements with x values, with start & end x values guaranteed.
-
- let mut result = Vec::with_capacity(self.entries.len());
- result.push(PiecewiseLinearFunctionEntry {
- x: self.entries[0].x.unwrap(),
- y: self.entries[0].y,
- });
- for (i, e) in self.entries.iter().enumerate().skip(1) {
- if e.x.is_none() {
- // Need to calculate x values by first finding an entry with the first
- // defined x value (Guaranteed to exist as the list end has it defined).
- continue;
- }
- // x is defined for this element.
- let divisor = i - result.len() + 1;
- // Any element(s) with undefined x to assign?
- if divisor != 1 {
- // Have at least one element in result at all times.
- let start_x = result.last().unwrap().x;
- let increment = (e.x.unwrap() - start_x) / divisor as ValueType;
- // Grab every element with undefined x to this point, which starts at the end of the result
- // array, and ending right before the current index. Then, assigned the evenly divided
- // x values.
- result.extend(
- self.entries[result.len()..i]
- .iter()
- .enumerate()
- .map(|(j, e)| {
- debug_assert!(e.x.is_none(), "Expected an entry with x undefined!");
- PiecewiseLinearFunctionEntry {
- x: increment * (j + 1) as ValueType + start_x,
- y: e.y,
- }
- }),
- );
- }
- result.push(PiecewiseLinearFunctionEntry {
- x: e.x.unwrap(),
- y: e.y,
- });
- }
- debug_assert_eq!(
- result.len(),
- self.entries.len(),
- "Should've mapped one-to-one!"
- );
- PiecewiseLinearFunction {
- entries: result.into(),
- }
- }
-}
diff --git a/components/style/properties/Mako-1.1.2-py2.py3-none-any.whl b/components/style/properties/Mako-1.1.2-py2.py3-none-any.whl
deleted file mode 100644
index 9593025a473..00000000000
--- a/components/style/properties/Mako-1.1.2-py2.py3-none-any.whl
+++ /dev/null
Binary files differ
diff --git a/components/style/properties/build.py b/components/style/properties/build.py
deleted file mode 100644
index c03d9023a7e..00000000000
--- a/components/style/properties/build.py
+++ /dev/null
@@ -1,172 +0,0 @@
-# This Source Code Form is subject to the terms of the Mozilla Public
-# License, v. 2.0. If a copy of the MPL was not distributed with this
-# file, You can obtain one at https://mozilla.org/MPL/2.0/.
-
-import json
-import os.path
-import re
-import sys
-
-BASE = os.path.dirname(__file__.replace("\\", "/"))
-sys.path.insert(0, os.path.join(BASE, "Mako-1.1.2-py2.py3-none-any.whl"))
-sys.path.insert(0, BASE) # For importing `data.py`
-
-from mako import exceptions
-from mako.lookup import TemplateLookup
-from mako.template import Template
-
-import data
-
-RE_PYTHON_ADDR = re.compile(r"<.+? object at 0x[0-9a-fA-F]+>")
-
-OUT_DIR = os.environ.get("OUT_DIR", "")
-
-STYLE_STRUCT_LIST = [
- "background",
- "border",
- "box",
- "column",
- "counters",
- "effects",
- "font",
- "inherited_box",
- "inherited_svg",
- "inherited_table",
- "inherited_text",
- "inherited_ui",
- "list",
- "margin",
- "outline",
- "page",
- "padding",
- "position",
- "svg",
- "table",
- "text",
- "ui",
- "xul",
-]
-
-
-def main():
- usage = (
- "Usage: %s [ servo | gecko ] [ style-crate | geckolib <template> | html ]"
- % sys.argv[0]
- )
- if len(sys.argv) < 3:
- abort(usage)
- engine = sys.argv[1]
- output = sys.argv[2]
-
- if engine not in ["servo", "gecko"] or output not in [
- "style-crate",
- "geckolib",
- "html",
- ]:
- abort(usage)
-
- properties = data.PropertiesData(engine=engine)
- files = {}
- for kind in ["longhands", "shorthands"]:
- files[kind] = {}
- for struct in STYLE_STRUCT_LIST:
- file_name = os.path.join(BASE, kind, "{}.mako.rs".format(struct))
- if kind == "shorthands" and not os.path.exists(file_name):
- files[kind][struct] = ""
- continue
- files[kind][struct] = render(
- file_name,
- engine=engine,
- data=properties,
- )
- properties_template = os.path.join(BASE, "properties.mako.rs")
- files["properties"] = render(
- properties_template,
- engine=engine,
- data=properties,
- __file__=properties_template,
- OUT_DIR=OUT_DIR,
- )
- if output == "style-crate":
- write(OUT_DIR, "properties.rs", files["properties"])
- for kind in ["longhands", "shorthands"]:
- for struct in files[kind]:
- write(
- os.path.join(OUT_DIR, kind),
- "{}.rs".format(struct),
- files[kind][struct],
- )
-
- if engine == "gecko":
- template = os.path.join(BASE, "gecko.mako.rs")
- rust = render(template, data=properties)
- write(OUT_DIR, "gecko_properties.rs", rust)
-
- if engine == "servo":
- properties_dict = {
- kind: {
- p.name: {"pref": getattr(p, "servo_pref")}
- for prop in properties_list
- if prop.enabled_in_content()
- for p in [prop] + prop.aliases
- }
- for kind, properties_list in [
- ("longhands", properties.longhands),
- ("shorthands", properties.shorthands),
- ]
- }
- as_html = render(
- os.path.join(BASE, "properties.html.mako"), properties=properties_dict
- )
- as_json = json.dumps(properties_dict, indent=4, sort_keys=True)
- doc_servo = os.path.join(BASE, "..", "..", "..", "target", "doc", "servo")
- write(doc_servo, "css-properties.html", as_html)
- write(doc_servo, "css-properties.json", as_json)
- write(OUT_DIR, "css-properties.json", as_json)
- elif output == "geckolib":
- if len(sys.argv) < 4:
- abort(usage)
- template = sys.argv[3]
- header = render(template, data=properties)
- sys.stdout.write(header)
-
-
-def abort(message):
- print(message, file=sys.stderr)
- sys.exit(1)
-
-
-def render(filename, **context):
- try:
- lookup = TemplateLookup(
- directories=[BASE], input_encoding="utf8", strict_undefined=True
- )
- template = Template(
- open(filename, "rb").read(),
- filename=filename,
- input_encoding="utf8",
- lookup=lookup,
- strict_undefined=True,
- )
- # Uncomment to debug generated Python code:
- # write("/tmp", "mako_%s.py" % os.path.basename(filename), template.code)
- return template.render(**context)
- except Exception:
- # Uncomment to see a traceback in generated Python code:
- # raise
- abort(exceptions.text_error_template().render())
-
-
-def write(directory, filename, content):
- if not os.path.exists(directory):
- os.makedirs(directory)
- full_path = os.path.join(directory, filename)
- open(full_path, "w", encoding="utf-8").write(content)
-
- python_addr = RE_PYTHON_ADDR.search(content)
- if python_addr:
- abort('Found "{}" in {} ({})'.format(python_addr.group(0), filename, full_path))
-
-
-if __name__ == "__main__":
- main()
diff --git a/components/style/properties/cascade.rs b/components/style/properties/cascade.rs
deleted file mode 100644
index f9951e2cfc5..00000000000
--- a/components/style/properties/cascade.rs
+++ /dev/null
@@ -1,1258 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
-
-//! The main cascading algorithm of the style system.
-
-use crate::applicable_declarations::CascadePriority;
-use crate::color::AbsoluteColor;
-use crate::computed_value_flags::ComputedValueFlags;
-use crate::context::QuirksMode;
-use crate::custom_properties::CustomPropertiesBuilder;
-use crate::dom::TElement;
-use crate::logical_geometry::WritingMode;
-use crate::media_queries::Device;
-use crate::properties::declaration_block::{DeclarationImportanceIterator, Importance};
-use crate::properties::generated::{
- CSSWideKeyword, ComputedValues, LonghandId, LonghandIdSet, PropertyDeclaration,
- PropertyDeclarationId, PropertyFlags, ShorthandsWithPropertyReferencesCache, StyleBuilder,
- CASCADE_PROPERTY,
-};
-use crate::rule_cache::{RuleCache, RuleCacheConditions};
-use crate::rule_tree::{CascadeLevel, StrongRuleNode};
-use crate::selector_parser::PseudoElement;
-use crate::shared_lock::StylesheetGuards;
-use crate::style_adjuster::StyleAdjuster;
-use crate::stylesheets::container_rule::ContainerSizeQuery;
-use crate::stylesheets::{layer_rule::LayerOrder, Origin};
-#[cfg(feature = "gecko")]
-use crate::values::specified::length::FontBaseSize;
-use crate::values::{computed, specified};
-use fxhash::FxHashMap;
-use servo_arc::Arc;
-use smallvec::SmallVec;
-use std::borrow::Cow;
-#[cfg(feature = "gecko")]
-use std::mem;
-
-#[derive(Clone, Copy, Debug, Eq, PartialEq)]
-enum CanHaveLogicalProperties {
- No,
- Yes,
-}
-
-/// Performs the CSS cascade, computing new styles for an element from its parent style.
-///
-/// The arguments are:
-///
-/// * `device`: Used to get the initial viewport and other external state.
-///
-/// * `rule_node`: The rule node in the tree that represent the CSS rules that
-/// matched.
-///
-/// * `parent_style`: The parent style, if applicable; if `None`, this is the root node.
-///
-/// Returns the computed values.
-/// * `flags`: Various flags.
-///
-pub fn cascade<E>(
- device: &Device,
- pseudo: Option<&PseudoElement>,
- rule_node: &StrongRuleNode,
- guards: &StylesheetGuards,
- originating_element_style: Option<&ComputedValues>,
- parent_style: Option<&ComputedValues>,
- parent_style_ignoring_first_line: Option<&ComputedValues>,
- layout_parent_style: Option<&ComputedValues>,
- visited_rules: Option<&StrongRuleNode>,
- cascade_input_flags: ComputedValueFlags,
- quirks_mode: QuirksMode,
- rule_cache: Option<&RuleCache>,
- rule_cache_conditions: &mut RuleCacheConditions,
- element: Option<E>,
-) -> Arc<ComputedValues>
-where
- E: TElement,
-{
- cascade_rules(
- device,
- pseudo,
- rule_node,
- guards,
- originating_element_style,
- parent_style,
- parent_style_ignoring_first_line,
- layout_parent_style,
- CascadeMode::Unvisited { visited_rules },
- cascade_input_flags,
- quirks_mode,
- rule_cache,
- rule_cache_conditions,
- element,
- )
-}
-
-struct DeclarationIterator<'a> {
- // Global to the iteration.
- guards: &'a StylesheetGuards<'a>,
- restriction: Option<PropertyFlags>,
- // The rule we're iterating over.
- current_rule_node: Option<&'a StrongRuleNode>,
- // Per rule state.
- declarations: DeclarationImportanceIterator<'a>,
- origin: Origin,
- importance: Importance,
- priority: CascadePriority,
-}
-
-impl<'a> DeclarationIterator<'a> {
- #[inline]
- fn new(
- rule_node: &'a StrongRuleNode,
- guards: &'a StylesheetGuards,
- pseudo: Option<&PseudoElement>,
- ) -> Self {
- let restriction = pseudo.and_then(|p| p.property_restriction());
- let mut iter = Self {
- guards,
- current_rule_node: Some(rule_node),
- origin: Origin::UserAgent,
- importance: Importance::Normal,
- priority: CascadePriority::new(CascadeLevel::UANormal, LayerOrder::root()),
- declarations: DeclarationImportanceIterator::default(),
- restriction,
- };
- iter.update_for_node(rule_node);
- iter
- }
-
- fn update_for_node(&mut self, node: &'a StrongRuleNode) {
- self.priority = node.cascade_priority();
- let level = self.priority.cascade_level();
- self.origin = level.origin();
- self.importance = level.importance();
- let guard = match self.origin {
- Origin::Author => self.guards.author,
- Origin::User | Origin::UserAgent => self.guards.ua_or_user,
- };
- self.declarations = match node.style_source() {
- Some(source) => source.read(guard).declaration_importance_iter(),
- None => DeclarationImportanceIterator::default(),
- };
- }
-}
-
-impl<'a> Iterator for DeclarationIterator<'a> {
- type Item = (&'a PropertyDeclaration, CascadePriority);
-
- #[inline]
- fn next(&mut self) -> Option<Self::Item> {
- loop {
- if let Some((decl, importance)) = self.declarations.next_back() {
- if self.importance != importance {
- continue;
- }
-
- if let Some(restriction) = self.restriction {
- // decl.id() is either a longhand or a custom
- // property. Custom properties are always allowed, but
- // longhands are only allowed if they have our
- // restriction flag set.
- if let PropertyDeclarationId::Longhand(id) = decl.id() {
- if !id.flags().contains(restriction) && self.origin != Origin::UserAgent {
- continue;
- }
- }
- }
-
- return Some((decl, self.priority));
- }
-
- let next_node = self.current_rule_node.take()?.parent()?;
- self.current_rule_node = Some(next_node);
- self.update_for_node(next_node);
- }
- }
-}
-
-fn cascade_rules<E>(
- device: &Device,
- pseudo: Option<&PseudoElement>,
- rule_node: &StrongRuleNode,
- guards: &StylesheetGuards,
- originating_element_style: Option<&ComputedValues>,
- parent_style: Option<&ComputedValues>,
- parent_style_ignoring_first_line: Option<&ComputedValues>,
- layout_parent_style: Option<&ComputedValues>,
- cascade_mode: CascadeMode,
- cascade_input_flags: ComputedValueFlags,
- quirks_mode: QuirksMode,
- rule_cache: Option<&RuleCache>,
- rule_cache_conditions: &mut RuleCacheConditions,
- element: Option<E>,
-) -> Arc<ComputedValues>
-where
- E: TElement,
-{
- debug_assert_eq!(
- parent_style.is_some(),
- parent_style_ignoring_first_line.is_some()
- );
- apply_declarations(
- device,
- pseudo,
- rule_node,
- guards,
- DeclarationIterator::new(rule_node, guards, pseudo),
- originating_element_style,
- parent_style,
- parent_style_ignoring_first_line,
- layout_parent_style,
- cascade_mode,
- cascade_input_flags,
- quirks_mode,
- rule_cache,
- rule_cache_conditions,
- element,
- )
-}
-
-/// Whether we're cascading for visited or unvisited styles.
-#[derive(Clone, Copy)]
-pub enum CascadeMode<'a> {
- /// We're cascading for unvisited styles.
- Unvisited {
- /// The visited rules that should match the visited style.
- visited_rules: Option<&'a StrongRuleNode>,
- },
- /// We're cascading for visited styles.
- Visited {
- /// The writing mode of our unvisited style, needed to correctly resolve
- /// logical properties..
- writing_mode: WritingMode,
- },
-}
-
-/// NOTE: This function expects the declaration with more priority to appear
-/// first.
-pub fn apply_declarations<'a, E, I>(
- device: &Device,
- pseudo: Option<&PseudoElement>,
- rules: &StrongRuleNode,
- guards: &StylesheetGuards,
- iter: I,
- originating_element_style: Option<&ComputedValues>,
- parent_style: Option<&ComputedValues>,
- parent_style_ignoring_first_line: Option<&ComputedValues>,
- layout_parent_style: Option<&ComputedValues>,
- cascade_mode: CascadeMode,
- cascade_input_flags: ComputedValueFlags,
- quirks_mode: QuirksMode,
- rule_cache: Option<&RuleCache>,
- rule_cache_conditions: &mut RuleCacheConditions,
- element: Option<E>,
-) -> Arc<ComputedValues>
-where
- E: TElement,
- I: Iterator<Item = (&'a PropertyDeclaration, CascadePriority)>,
-{
- debug_assert_eq!(
- originating_element_style.is_some(),
- element.is_some() && pseudo.is_some()
- );
- debug_assert!(layout_parent_style.is_none() || parent_style.is_some());
- debug_assert_eq!(
- parent_style.is_some(),
- parent_style_ignoring_first_line.is_some()
- );
- #[cfg(feature = "gecko")]
- debug_assert!(
- parent_style.is_none() ||
- ::std::ptr::eq(
- parent_style.unwrap(),
- parent_style_ignoring_first_line.unwrap()
- ) ||
- parent_style.unwrap().is_first_line_style()
- );
-
- let inherited_style = parent_style.unwrap_or(device.default_computed_values());
-
- let mut declarations = SmallVec::<[(&_, CascadePriority); 32]>::new();
- let mut referenced_properties = LonghandIdSet::default();
- let custom_properties = {
- let mut builder = CustomPropertiesBuilder::new(inherited_style.custom_properties(), device);
-
- for (declaration, priority) in iter {
- declarations.push((declaration, priority));
- if let PropertyDeclaration::Custom(ref declaration) = *declaration {
- builder.cascade(declaration, priority);
- } else {
- referenced_properties.insert(declaration.id().as_longhand().unwrap());
- }
- }
-
- builder.build()
- };
-
- let is_root_element = pseudo.is_none() && element.map_or(false, |e| e.is_root());
- let container_size_query =
- ContainerSizeQuery::for_option_element(element, originating_element_style);
-
- let mut context = computed::Context::new(
- // We'd really like to own the rules here to avoid refcount traffic, but
- // animation's usage of `apply_declarations` make this tricky. See bug
- // 1375525.
- StyleBuilder::new(
- device,
- parent_style,
- parent_style_ignoring_first_line,
- pseudo,
- Some(rules.clone()),
- custom_properties,
- is_root_element,
- ),
- quirks_mode,
- rule_cache_conditions,
- container_size_query,
- );
-
- context.style().add_flags(cascade_input_flags);
-
- let using_cached_reset_properties;
- let mut cascade = Cascade::new(&mut context, cascade_mode, &referenced_properties);
- let mut shorthand_cache = ShorthandsWithPropertyReferencesCache::default();
-
- let properties_to_apply = match cascade.cascade_mode {
- CascadeMode::Visited { writing_mode } => {
- cascade.context.builder.writing_mode = writing_mode;
- // We never insert visited styles into the cache so we don't need to
- // try looking it up. It also wouldn't be super-profitable, only a
- // handful reset properties are non-inherited.
- using_cached_reset_properties = false;
- LonghandIdSet::visited_dependent()
- },
- CascadeMode::Unvisited { visited_rules } => {
- if cascade.apply_properties(
- CanHaveLogicalProperties::No,
- LonghandIdSet::writing_mode_group(),
- declarations.iter().cloned(),
- &mut shorthand_cache,
- ) {
- cascade.compute_writing_mode();
- }
-
- if cascade.apply_properties(
- CanHaveLogicalProperties::No,
- LonghandIdSet::fonts_and_color_group(),
- declarations.iter().cloned(),
- &mut shorthand_cache,
- ) {
- cascade.fixup_font_stuff();
- }
-
- if let Some(visited_rules) = visited_rules {
- cascade.compute_visited_style_if_needed(
- element,
- originating_element_style,
- parent_style,
- parent_style_ignoring_first_line,
- layout_parent_style,
- visited_rules,
- guards,
- );
- }
-
- using_cached_reset_properties =
- cascade.try_to_use_cached_reset_properties(rule_cache, guards);
-
- if using_cached_reset_properties {
- LonghandIdSet::late_group_only_inherited()
- } else {
- LonghandIdSet::late_group()
- }
- },
- };
-
- cascade.apply_properties(
- CanHaveLogicalProperties::Yes,
- properties_to_apply,
- declarations.iter().cloned(),
- &mut shorthand_cache,
- );
-
- cascade.finished_applying_properties();
-
- context.builder.clear_modified_reset();
-
- if matches!(cascade_mode, CascadeMode::Unvisited { .. }) {
- StyleAdjuster::new(&mut context.builder)
- .adjust(layout_parent_style.unwrap_or(inherited_style), element);
- }
-
- if context.builder.modified_reset() || using_cached_reset_properties {
- // If we adjusted any reset structs, we can't cache this ComputedValues.
- //
- // Also, if we re-used existing reset structs, don't bother caching it
- // back again. (Aside from being wasted effort, it will be wrong, since
- // context.rule_cache_conditions won't be set appropriately if we didn't
- // compute those reset properties.)
- context.rule_cache_conditions.borrow_mut().set_uncacheable();
- }
-
- context.builder.build()
-}
-
-/// For ignored colors mode, we sometimes want to do something equivalent to
-/// "revert-or-initial", where we `revert` for a given origin, but then apply a
-/// given initial value if nothing in other origins did override it.
-///
-/// This is a bit of a clunky way of achieving this.
-type DeclarationsToApplyUnlessOverriden = SmallVec<[PropertyDeclaration; 2]>;
-
-fn tweak_when_ignoring_colors(
- context: &computed::Context,
- longhand_id: LonghandId,
- origin: Origin,
- declaration: &mut Cow<PropertyDeclaration>,
- declarations_to_apply_unless_overriden: &mut DeclarationsToApplyUnlessOverriden,
-) {
- use crate::values::computed::ToComputedValue;
- use crate::values::specified::Color;
-
- if !longhand_id.ignored_when_document_colors_disabled() {
- return;
- }
-
- let is_ua_or_user_rule = matches!(origin, Origin::User | Origin::UserAgent);
- if is_ua_or_user_rule {
- return;
- }
-
- // Always honor colors if forced-color-adjust is set to none.
- #[cfg(feature = "gecko")] {
- let forced = context
- .builder
- .get_inherited_text()
- .clone_forced_color_adjust();
- if forced == computed::ForcedColorAdjust::None {
- return;
- }
- }
-
- // Don't override background-color on ::-moz-color-swatch. It is set as an
- // author style (via the style attribute), but it's pretty important for it
- // to show up for obvious reasons :)
- if context
- .builder
- .pseudo
- .map_or(false, |p| p.is_color_swatch()) &&
- longhand_id == LonghandId::BackgroundColor
- {
- return;
- }
-
- fn alpha_channel(color: &Color, context: &computed::Context) -> f32 {
- // We assume here currentColor is opaque.
- color
- .to_computed_value(context)
- .resolve_to_absolute(&AbsoluteColor::black())
- .alpha
- }
-
- // A few special-cases ahead.
- match **declaration {
- PropertyDeclaration::BackgroundColor(ref color) => {
- // We honor system colors and transparent colors unconditionally.
- //
- // NOTE(emilio): We honor transparent unconditionally, like we do
- // for color, even though it causes issues like bug 1625036. The
- // reasoning is that the conditions that trigger that (having
- // mismatched widget and default backgrounds) are both uncommon, and
- // broken in other applications as well, and not honoring
- // transparent makes stuff uglier or break unconditionally
- // (bug 1666059, bug 1755713).
- if color.honored_in_forced_colors_mode(/* allow_transparent = */ true) {
- return;
- }
- // For background-color, we revert or initial-with-preserved-alpha
- // otherwise, this is needed to preserve semi-transparent
- // backgrounds.
- let alpha = alpha_channel(color, context);
- if alpha == 0.0 {
- return;
- }
- let mut color = context.builder.device.default_background_color();
- color.alpha = alpha;
- declarations_to_apply_unless_overriden
- .push(PropertyDeclaration::BackgroundColor(color.into()))
- },
- PropertyDeclaration::Color(ref color) => {
- // We honor color: transparent and system colors.
- if color
- .0
- .honored_in_forced_colors_mode(/* allow_transparent = */ true)
- {
- return;
- }
- // If the inherited color would be transparent, but we would
- // override this with a non-transparent color, then override it with
- // the default color. Otherwise just let it inherit through.
- if context
- .builder
- .get_parent_inherited_text()
- .clone_color()
- .alpha ==
- 0.0
- {
- let color = context.builder.device.default_color();
- declarations_to_apply_unless_overriden.push(PropertyDeclaration::Color(
- specified::ColorPropertyValue(color.into()),
- ))
- }
- },
- // We honor url background-images if backplating.
- #[cfg(feature = "gecko")]
- PropertyDeclaration::BackgroundImage(ref bkg) => {
- use crate::values::generics::image::Image;
- if static_prefs::pref!("browser.display.permit_backplate") {
- if bkg
- .0
- .iter()
- .all(|image| matches!(*image, Image::Url(..) | Image::None))
- {
- return;
- }
- }
- },
- _ => {
- // We honor system colors more generally for all colors.
- //
- // We used to honor transparent but that causes accessibility
- // regressions like bug 1740924.
- //
- // NOTE(emilio): This doesn't handle caret-color and accent-color
- // because those use a slightly different syntax (<color> | auto for
- // example).
- //
- // That's probably fine though, as using a system color for
- // caret-color doesn't make sense (using currentColor is fine), and
- // we ignore accent-color in high-contrast-mode anyways.
- if let Some(color) = declaration.color_value() {
- if color.honored_in_forced_colors_mode(/* allow_transparent = */ false) {
- return;
- }
- }
- },
- }
-
- *declaration.to_mut() =
- PropertyDeclaration::css_wide_keyword(longhand_id, CSSWideKeyword::Revert);
-}
-
-struct Cascade<'a, 'b: 'a> {
- context: &'a mut computed::Context<'b>,
- cascade_mode: CascadeMode<'a>,
- /// All the properties that have a declaration in the cascade.
- referenced: &'a LonghandIdSet,
- seen: LonghandIdSet,
- author_specified: LonghandIdSet,
- reverted_set: LonghandIdSet,
- reverted: FxHashMap<LonghandId, (CascadePriority, bool)>,
-}
-
-impl<'a, 'b: 'a> Cascade<'a, 'b> {
- fn new(
- context: &'a mut computed::Context<'b>,
- cascade_mode: CascadeMode<'a>,
- referenced: &'a LonghandIdSet,
- ) -> Self {
- Self {
- context,
- cascade_mode,
- referenced,
- seen: LonghandIdSet::default(),
- author_specified: LonghandIdSet::default(),
- reverted_set: Default::default(),
- reverted: Default::default(),
- }
- }
-
- fn substitute_variables_if_needed<'decl, 'cache>(
- &mut self,
- declaration: &'decl PropertyDeclaration,
- cache: &'cache mut ShorthandsWithPropertyReferencesCache,
- ) -> Cow<'decl, PropertyDeclaration>
- where
- 'cache: 'decl,
- {
- let declaration = match *declaration {
- PropertyDeclaration::WithVariables(ref declaration) => declaration,
- ref d => return Cow::Borrowed(d),
- };
-
- if !declaration.id.inherited() {
- self.context
- .rule_cache_conditions
- .borrow_mut()
- .set_uncacheable();
-
- // NOTE(emilio): We only really need to add the `display` /
- // `content` flag if the CSS variable has not been specified on our
- // declarations, but we don't have that information at this point,
- // and it doesn't seem like an important enough optimization to
- // warrant it.
- match declaration.id {
- LonghandId::Display => {
- self.context
- .builder
- .add_flags(ComputedValueFlags::DISPLAY_DEPENDS_ON_INHERITED_STYLE);
- },
- LonghandId::Content => {
- self.context
- .builder
- .add_flags(ComputedValueFlags::CONTENT_DEPENDS_ON_INHERITED_STYLE);
- },
- _ => {},
- }
- }
-
- declaration.value.substitute_variables(
- declaration.id,
- self.context.builder.writing_mode,
- self.context.builder.custom_properties(),
- self.context.quirks_mode,
- self.context.device(),
- cache,
- )
- }
-
- #[inline(always)]
- fn apply_declaration(&mut self, longhand_id: LonghandId, declaration: &PropertyDeclaration) {
- // We could (and used to) use a pattern match here, but that bloats this
- // function to over 100K of compiled code!
- //
- // To improve i-cache behavior, we outline the individual functions and
- // use virtual dispatch instead.
- let discriminant = longhand_id as usize;
- (CASCADE_PROPERTY[discriminant])(declaration, &mut self.context);
- }
-
- fn apply_properties<'decls, I>(
- &mut self,
- can_have_logical_properties: CanHaveLogicalProperties,
- properties_to_apply: &'a LonghandIdSet,
- declarations: I,
- mut shorthand_cache: &mut ShorthandsWithPropertyReferencesCache,
- ) -> bool
- where
- I: Iterator<Item = (&'decls PropertyDeclaration, CascadePriority)>,
- {
- if !self.referenced.contains_any(properties_to_apply) {
- return false;
- }
-
- let can_have_logical_properties =
- can_have_logical_properties == CanHaveLogicalProperties::Yes;
-
- let ignore_colors = !self.context.builder.device.use_document_colors();
- let mut declarations_to_apply_unless_overriden = DeclarationsToApplyUnlessOverriden::new();
-
- for (declaration, priority) in declarations {
- let origin = priority.cascade_level().origin();
-
- let declaration_id = declaration.id();
- let longhand_id = match declaration_id {
- PropertyDeclarationId::Longhand(id) => id,
- PropertyDeclarationId::Custom(..) => continue,
- };
-
- if !properties_to_apply.contains(longhand_id) {
- continue;
- }
-
- debug_assert!(can_have_logical_properties || !longhand_id.is_logical());
- let physical_longhand_id = if can_have_logical_properties {
- longhand_id.to_physical(self.context.builder.writing_mode)
- } else {
- longhand_id
- };
-
- if self.seen.contains(physical_longhand_id) {
- continue;
- }
-
- if self.reverted_set.contains(physical_longhand_id) {
- if let Some(&(reverted_priority, is_origin_revert)) =
- self.reverted.get(&physical_longhand_id)
- {
- if !reverted_priority.allows_when_reverted(&priority, is_origin_revert) {
- continue;
- }
- }
- }
-
- let mut declaration =
- self.substitute_variables_if_needed(declaration, &mut shorthand_cache);
-
- // When document colors are disabled, do special handling of
- // properties that are marked as ignored in that mode.
- if ignore_colors {
- tweak_when_ignoring_colors(
- &self.context,
- longhand_id,
- origin,
- &mut declaration,
- &mut declarations_to_apply_unless_overriden,
- );
- debug_assert_eq!(
- declaration.id(),
- PropertyDeclarationId::Longhand(longhand_id),
- "Shouldn't change the declaration id!",
- );
- }
-
- let is_unset = match declaration.get_css_wide_keyword() {
- Some(keyword) => match keyword {
- CSSWideKeyword::RevertLayer | CSSWideKeyword::Revert => {
- let origin_revert = keyword == CSSWideKeyword::Revert;
- // We intentionally don't want to insert it into
- // `self.seen`, `reverted` takes care of rejecting other
- // declarations as needed.
- self.reverted_set.insert(physical_longhand_id);
- self.reverted
- .insert(physical_longhand_id, (priority, origin_revert));
- continue;
- },
- CSSWideKeyword::Unset => true,
- CSSWideKeyword::Inherit => longhand_id.inherited(),
- CSSWideKeyword::Initial => !longhand_id.inherited(),
- },
- None => false,
- };
-
- self.seen.insert(physical_longhand_id);
- if origin == Origin::Author {
- self.author_specified.insert(physical_longhand_id);
- }
-
- if is_unset {
- continue;
- }
-
- // FIXME(emilio): We should avoid generating code for logical
- // longhands and just use the physical ones, then rename
- // physical_longhand_id to just longhand_id.
- self.apply_declaration(longhand_id, &*declaration);
- }
-
- if ignore_colors {
- for declaration in declarations_to_apply_unless_overriden.iter() {
- let longhand_id = match declaration.id() {
- PropertyDeclarationId::Longhand(id) => id,
- PropertyDeclarationId::Custom(..) => unreachable!(),
- };
- debug_assert!(!longhand_id.is_logical());
- if self.seen.contains(longhand_id) {
- continue;
- }
- self.apply_declaration(longhand_id, declaration);
- }
- }
-
- true
- }
-
- fn compute_writing_mode(&mut self) {
- debug_assert!(matches!(self.cascade_mode, CascadeMode::Unvisited { .. }));
- self.context.builder.writing_mode =
- WritingMode::new(self.context.builder.get_inherited_box())
- }
-
- fn compute_visited_style_if_needed<E>(
- &mut self,
- element: Option<E>,
- originating_element_style: Option<&ComputedValues>,
- parent_style: Option<&ComputedValues>,
- parent_style_ignoring_first_line: Option<&ComputedValues>,
- layout_parent_style: Option<&ComputedValues>,
- visited_rules: &StrongRuleNode,
- guards: &StylesheetGuards,
- ) where
- E: TElement,
- {
- debug_assert!(matches!(self.cascade_mode, CascadeMode::Unvisited { .. }));
- let is_link = self.context.builder.pseudo.is_none() && element.unwrap().is_link();
-
- macro_rules! visited_parent {
- ($parent:expr) => {
- if is_link {
- $parent
- } else {
- $parent.map(|p| p.visited_style().unwrap_or(p))
- }
- };
- }
-
- let writing_mode = self.context.builder.writing_mode;
-
- // We could call apply_declarations directly, but that'd cause
- // another instantiation of this function which is not great.
- let style = cascade_rules(
- self.context.builder.device,
- self.context.builder.pseudo,
- visited_rules,
- guards,
- visited_parent!(originating_element_style),
- visited_parent!(parent_style),
- visited_parent!(parent_style_ignoring_first_line),
- visited_parent!(layout_parent_style),
- CascadeMode::Visited { writing_mode },
- // Cascade input flags don't matter for the visited style, they are
- // in the main (unvisited) style.
- Default::default(),
- self.context.quirks_mode,
- // The rule cache doesn't care about caching :visited
- // styles, we cache the unvisited style instead. We still do
- // need to set the caching dependencies properly if present
- // though, so the cache conditions need to match.
- None, // rule_cache
- &mut *self.context.rule_cache_conditions.borrow_mut(),
- element,
- );
- self.context.builder.visited_style = Some(style);
- }
-
- fn finished_applying_properties(&mut self) {
- let builder = &mut self.context.builder;
-
- #[cfg(feature = "gecko")]
- {
- if let Some(bg) = builder.get_background_if_mutated() {
- bg.fill_arrays();
- }
-
- if let Some(svg) = builder.get_svg_if_mutated() {
- svg.fill_arrays();
- }
- }
-
- if self
- .author_specified
- .contains_any(LonghandIdSet::border_background_properties())
- {
- builder.add_flags(ComputedValueFlags::HAS_AUTHOR_SPECIFIED_BORDER_BACKGROUND);
- }
-
- if self.author_specified.contains(LonghandId::FontFamily) {
- builder.add_flags(ComputedValueFlags::HAS_AUTHOR_SPECIFIED_FONT_FAMILY);
- }
-
- if self.author_specified.contains(LonghandId::LetterSpacing) {
- builder.add_flags(ComputedValueFlags::HAS_AUTHOR_SPECIFIED_LETTER_SPACING);
- }
-
- if self.author_specified.contains(LonghandId::WordSpacing) {
- builder.add_flags(ComputedValueFlags::HAS_AUTHOR_SPECIFIED_WORD_SPACING);
- }
-
- #[cfg(feature = "gecko")]
- if self
- .author_specified
- .contains(LonghandId::FontSynthesisWeight)
- {
- builder.add_flags(ComputedValueFlags::HAS_AUTHOR_SPECIFIED_FONT_SYNTHESIS_WEIGHT);
- }
-
- #[cfg(feature = "gecko")]
- if self
- .author_specified
- .contains(LonghandId::FontSynthesisStyle)
- {
- builder.add_flags(ComputedValueFlags::HAS_AUTHOR_SPECIFIED_FONT_SYNTHESIS_STYLE);
- }
-
- #[cfg(feature = "servo")]
- {
- if let Some(font) = builder.get_font_if_mutated() {
- font.compute_font_hash();
- }
- }
- }
-
- fn try_to_use_cached_reset_properties(
- &mut self,
- cache: Option<&'b RuleCache>,
- guards: &StylesheetGuards,
- ) -> bool {
- let cache = match cache {
- Some(cache) => cache,
- None => return false,
- };
-
- let builder = &mut self.context.builder;
-
- let cached_style = match cache.find(guards, &builder) {
- Some(style) => style,
- None => return false,
- };
-
- builder.copy_reset_from(cached_style);
-
- // We're using the same reset style as another element, and we'll skip
- // applying the relevant properties. So we need to do the relevant
- // bookkeeping here to keep these bits correct.
- //
- // Note that the border/background properties are non-inherited, so we
- // don't need to do anything else other than just copying the bits over.
- //
- // When using this optimization, we also need to copy whether the old
- // style specified viewport units / used font-relative lengths, this one
- // would as well. It matches the same rules, so it is the right thing
- // to do anyways, even if it's only used on inherited properties.
- let bits_to_copy = ComputedValueFlags::HAS_AUTHOR_SPECIFIED_BORDER_BACKGROUND |
- ComputedValueFlags::DEPENDS_ON_SELF_FONT_METRICS |
- ComputedValueFlags::DEPENDS_ON_INHERITED_FONT_METRICS |
- ComputedValueFlags::USES_VIEWPORT_UNITS;
- builder.add_flags(cached_style.flags & bits_to_copy);
-
- true
- }
-
- /// The initial font depends on the current lang group so we may need to
- /// recompute it if the language changed.
- #[inline]
- #[cfg(feature = "gecko")]
- fn recompute_initial_font_family_if_needed(&mut self) {
- use crate::gecko_bindings::bindings;
- use crate::values::computed::font::FontFamily;
-
- if !self.seen.contains(LonghandId::XLang) {
- return;
- }
-
- let builder = &mut self.context.builder;
- let default_font_type = {
- let font = builder.get_font();
-
- if !font.mFont.family.is_initial {
- return;
- }
-
- let default_font_type = unsafe {
- bindings::Gecko_nsStyleFont_ComputeFallbackFontTypeForLanguage(
- builder.device.document(),
- font.mLanguage.mRawPtr,
- )
- };
-
- let initial_generic = font.mFont.family.families.single_generic();
- debug_assert!(
- initial_generic.is_some(),
- "Initial font should be just one generic font"
- );
- if initial_generic == Some(default_font_type) {
- return;
- }
-
- default_font_type
- };
-
- // NOTE: Leaves is_initial untouched.
- builder.mutate_font().mFont.family.families =
- FontFamily::generic(default_font_type).families.clone();
- }
-
- /// Prioritize user fonts if needed by pref.
- #[inline]
- #[cfg(feature = "gecko")]
- fn prioritize_user_fonts_if_needed(&mut self) {
- use crate::gecko_bindings::bindings;
-
- if !self.seen.contains(LonghandId::FontFamily) {
- return;
- }
-
- if static_prefs::pref!("browser.display.use_document_fonts") != 0 {
- return;
- }
-
- let builder = &mut self.context.builder;
- let default_font_type = {
- let font = builder.get_font();
-
- if font.mFont.family.is_system_font {
- return;
- }
-
- if !font.mFont.family.families.needs_user_font_prioritization() {
- return;
- }
-
- unsafe {
- bindings::Gecko_nsStyleFont_ComputeFallbackFontTypeForLanguage(
- builder.device.document(),
- font.mLanguage.mRawPtr,
- )
- }
- };
-
- let font = builder.mutate_font();
- font.mFont
- .family
- .families
- .prioritize_first_generic_or_prepend(default_font_type);
- }
-
- /// Some keyword sizes depend on the font family and language.
- #[cfg(feature = "gecko")]
- fn recompute_keyword_font_size_if_needed(&mut self) {
- use crate::values::computed::ToComputedValue;
-
- if !self.seen.contains(LonghandId::XLang) && !self.seen.contains(LonghandId::FontFamily) {
- return;
- }
-
- let new_size = {
- let font = self.context.builder.get_font();
- let info = font.clone_font_size().keyword_info;
- let new_size = match info.kw {
- specified::FontSizeKeyword::None => return,
- _ => {
- self.context.for_non_inherited_property = false;
- specified::FontSize::Keyword(info).to_computed_value(self.context)
- },
- };
-
- if font.mScriptUnconstrainedSize == new_size.computed_size {
- return;
- }
-
- new_size
- };
-
- self.context.builder.mutate_font().set_font_size(new_size);
- }
-
- /// Some properties, plus setting font-size itself, may make us go out of
- /// our minimum font-size range.
- #[cfg(feature = "gecko")]
- fn constrain_font_size_if_needed(&mut self) {
- use crate::gecko_bindings::bindings;
- use crate::values::generics::NonNegative;
-
- if !self.seen.contains(LonghandId::XLang) &&
- !self.seen.contains(LonghandId::FontFamily) &&
- !self.seen.contains(LonghandId::MozMinFontSizeRatio) &&
- !self.seen.contains(LonghandId::FontSize)
- {
- return;
- }
-
- let builder = &mut self.context.builder;
- let min_font_size = {
- let font = builder.get_font();
- let min_font_size = unsafe {
- bindings::Gecko_nsStyleFont_ComputeMinSize(&**font, builder.device.document())
- };
-
- if font.mFont.size.0 >= min_font_size {
- return;
- }
-
- NonNegative(min_font_size)
- };
-
- builder.mutate_font().mFont.size = min_font_size;
- }
-
- /// <svg:text> is not affected by text zoom, and it uses a preshint to disable it. We fix up
- /// the struct when this happens by unzooming its contained font values, which will have been
- /// zoomed in the parent.
- ///
- /// FIXME(emilio): Why doing this _before_ handling font-size? That sounds wrong.
- #[cfg(feature = "gecko")]
- fn unzoom_fonts_if_needed(&mut self) {
- if !self.seen.contains(LonghandId::XTextScale) {
- return;
- }
-
- let builder = &mut self.context.builder;
-
- let parent_text_scale = builder.get_parent_font().clone__x_text_scale();
- let text_scale = builder.get_font().clone__x_text_scale();
- if parent_text_scale == text_scale {
- return;
- }
- debug_assert_ne!(
- parent_text_scale.text_zoom_enabled(),
- text_scale.text_zoom_enabled(),
- "There's only one value that disables it"
- );
- debug_assert!(
- !text_scale.text_zoom_enabled(),
- "We only ever disable text zoom (in svg:text), never enable it"
- );
- let device = builder.device;
- builder.mutate_font().unzoom_fonts(device);
- }
-
- /// MathML script* attributes do some very weird shit with font-size.
- ///
- /// Handle them specially here, separate from other font-size stuff.
- ///
- /// How this should interact with lang="" and font-family-dependent sizes is
- /// not clear to me. For now just pretend those don't exist here.
- #[cfg(feature = "gecko")]
- fn handle_mathml_scriptlevel_if_needed(&mut self) {
- use crate::values::generics::NonNegative;
-
- if !self.seen.contains(LonghandId::MathDepth) &&
- !self.seen.contains(LonghandId::MozScriptMinSize) &&
- !self.seen.contains(LonghandId::MozScriptSizeMultiplier)
- {
- return;
- }
-
- // If the user specifies a font-size, just let it be.
- if self.seen.contains(LonghandId::FontSize) {
- return;
- }
-
- const SCALE_FACTOR_WHEN_INCREMENTING_MATH_DEPTH_BY_ONE: f32 = 0.71;
-
- // Helper function that calculates the scale factor applied to font-size
- // when math-depth goes from parent_math_depth to computed_math_depth.
- // This function is essentially a modification of the MathML3's formula
- // 0.71^(parent_math_depth - computed_math_depth) so that a scale factor
- // of parent_script_percent_scale_down is applied when math-depth goes
- // from 0 to 1 and parent_script_script_percent_scale_down is applied
- // when math-depth goes from 0 to 2. This is also a straightforward
- // implementation of the specification's algorithm:
- // https://w3c.github.io/mathml-core/#the-math-script-level-property
- fn scale_factor_for_math_depth_change(
- parent_math_depth: i32,
- computed_math_depth: i32,
- parent_script_percent_scale_down: Option<f32>,
- parent_script_script_percent_scale_down: Option<f32>,
- ) -> f32 {
- let mut a = parent_math_depth;
- let mut b = computed_math_depth;
- let c = SCALE_FACTOR_WHEN_INCREMENTING_MATH_DEPTH_BY_ONE;
- let scale_between_0_and_1 = parent_script_percent_scale_down.unwrap_or_else(|| c);
- let scale_between_0_and_2 =
- parent_script_script_percent_scale_down.unwrap_or_else(|| c * c);
- let mut s = 1.0;
- let mut invert_scale_factor = false;
- if a == b {
- return s;
- }
- if b < a {
- mem::swap(&mut a, &mut b);
- invert_scale_factor = true;
- }
- let mut e = b - a;
- if a <= 0 && b >= 2 {
- s *= scale_between_0_and_2;
- e -= 2;
- } else if a == 1 {
- s *= scale_between_0_and_2 / scale_between_0_and_1;
- e -= 1;
- } else if b == 1 {
- s *= scale_between_0_and_1;
- e -= 1;
- }
- s *= (c as f32).powi(e);
- if invert_scale_factor {
- 1.0 / s.max(f32::MIN_POSITIVE)
- } else {
- s
- }
- }
-
- let (new_size, new_unconstrained_size) = {
- let builder = &self.context.builder;
- let font = builder.get_font();
- let parent_font = builder.get_parent_font();
-
- let delta = font.mMathDepth.saturating_sub(parent_font.mMathDepth);
-
- if delta == 0 {
- return;
- }
-
- let mut min = parent_font.mScriptMinSize;
- if font.mXTextScale.text_zoom_enabled() {
- min = builder.device.zoom_text(min);
- }
-
- // If the scriptsizemultiplier has been set to something other than
- // the default scale, use MathML3's implementation for backward
- // compatibility. Otherwise, follow MathML Core's algorithm.
- let scale = if parent_font.mScriptSizeMultiplier !=
- SCALE_FACTOR_WHEN_INCREMENTING_MATH_DEPTH_BY_ONE
- {
- (parent_font.mScriptSizeMultiplier as f32).powi(delta as i32)
- } else {
- // Script scale factors are independent of orientation.
- let font_metrics = self.context.query_font_metrics(
- FontBaseSize::InheritedStyle,
- FontMetricsOrientation::Horizontal,
- /* retrieve_math_scales = */ true,
- );
- scale_factor_for_math_depth_change(
- parent_font.mMathDepth as i32,
- font.mMathDepth as i32,
- font_metrics.script_percent_scale_down,
- font_metrics.script_script_percent_scale_down,
- )
- };
-
- let parent_size = parent_font.mSize.0;
- let parent_unconstrained_size = parent_font.mScriptUnconstrainedSize.0;
- let new_size = parent_size.scale_by(scale);
- let new_unconstrained_size = parent_unconstrained_size.scale_by(scale);
-
- if scale <= 1. {
- // The parent size can be smaller than scriptminsize, e.g. if it
- // was specified explicitly. Don't scale in this case, but we
- // don't want to set it to scriptminsize either since that will
- // make it larger.
- if parent_size <= min {
- (parent_size, new_unconstrained_size)
- } else {
- (min.max(new_size), new_unconstrained_size)
- }
- } else {
- // If the new unconstrained size is larger than the min size,
- // this means we have escaped the grasp of scriptminsize and can
- // revert to using the unconstrained size.
- // However, if the new size is even larger (perhaps due to usage
- // of em units), use that instead.
- (
- new_size.min(new_unconstrained_size.max(min)),
- new_unconstrained_size,
- )
- }
- };
- let font = self.context.builder.mutate_font();
- font.mFont.size = NonNegative(new_size);
- font.mSize = NonNegative(new_size);
- font.mScriptUnconstrainedSize = NonNegative(new_unconstrained_size);
- }
-
- /// Various properties affect how font-size and font-family are computed.
- ///
- /// These need to be handled here, since relative lengths and ex / ch units
- /// for late properties depend on these.
- fn fixup_font_stuff(&mut self) {
- #[cfg(feature = "gecko")]
- {
- self.unzoom_fonts_if_needed();
- self.recompute_initial_font_family_if_needed();
- self.prioritize_user_fonts_if_needed();
- self.recompute_keyword_font_size_if_needed();
- self.handle_mathml_scriptlevel_if_needed();
- self.constrain_font_size_if_needed()
- }
- }
-}
diff --git a/components/style/properties/computed_value_flags.rs b/components/style/properties/computed_value_flags.rs
deleted file mode 100644
index f12760aa8b4..00000000000
--- a/components/style/properties/computed_value_flags.rs
+++ /dev/null
@@ -1,190 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
-
-//! Misc information about a given computed style.
-
-bitflags! {
- /// Misc information about a given computed style.
- ///
- /// All flags are currently inherited for text, pseudo elements, and
- /// anonymous boxes, see StyleBuilder::for_inheritance and its callsites.
- /// If we ever want to add some flags that shouldn't inherit for them,
- /// we might want to add a function to handle this.
- #[repr(C)]
- pub struct ComputedValueFlags: u32 {
- /// Whether the style or any of the ancestors has a text-decoration-line
- /// property that should get propagated to descendants.
- ///
- /// text-decoration-line is a reset property, but gets propagated in the
- /// frame/box tree.
- const HAS_TEXT_DECORATION_LINES = 1 << 0;
-
- /// Whether line break inside should be suppressed.
- ///
- /// If this flag is set, the line should not be broken inside,
- /// which means inlines act as if nowrap is set, <br> element is
- /// suppressed, and blocks are inlinized.
- ///
- /// This bit is propagated to all children of line participants.
- /// It is currently used by ruby to make its content unbreakable.
- const SHOULD_SUPPRESS_LINEBREAK = 1 << 1;
-
- /// A flag used to mark text that that has text-combine-upright.
- ///
- /// This is used from Gecko's layout engine.
- const IS_TEXT_COMBINED = 1 << 2;
-
- /// A flag used to mark styles under a relevant link that is also
- /// visited.
- const IS_RELEVANT_LINK_VISITED = 1 << 3;
-
- /// A flag used to mark styles which are a pseudo-element or under one.
- const IS_IN_PSEUDO_ELEMENT_SUBTREE = 1 << 4;
-
- /// A flag used to mark styles which have contain:style or under one.
- const SELF_OR_ANCESTOR_HAS_CONTAIN_STYLE = 1 << 5;
-
- /// Whether this style's `display` property depends on our parent style.
- ///
- /// This is important because it may affect our optimizations to avoid
- /// computing the style of pseudo-elements, given whether the
- /// pseudo-element is generated depends on the `display` value.
- const DISPLAY_DEPENDS_ON_INHERITED_STYLE = 1 << 6;
-
- /// Whether this style's `content` depends on our parent style.
- ///
- /// Important because of the same reason.
- const CONTENT_DEPENDS_ON_INHERITED_STYLE = 1 << 7;
-
- /// Whether the child explicitly inherits any reset property.
- const INHERITS_RESET_STYLE = 1 << 8;
-
- /// Whether any value on our style is font-metric-dependent on our
- /// primary font.
- const DEPENDS_ON_SELF_FONT_METRICS = 1 << 9;
-
- /// Whether any value on our style is font-metric-dependent on the
- /// primary font of our parent.
- const DEPENDS_ON_INHERITED_FONT_METRICS = 1 << 10;
-
- /// Whether the style or any of the ancestors has a multicol style.
- ///
- /// Only used in Servo.
- const CAN_BE_FRAGMENTED = 1 << 11;
-
- /// Whether this style is the style of the document element.
- const IS_ROOT_ELEMENT_STYLE = 1 << 12;
-
- /// Whether this element is inside an `opacity: 0` subtree.
- const IS_IN_OPACITY_ZERO_SUBTREE = 1 << 13;
-
- /// Whether there are author-specified rules for border-* properties
- /// (except border-image-*), background-color, or background-image.
- ///
- /// TODO(emilio): Maybe do include border-image, see:
- ///
- /// https://github.com/w3c/csswg-drafts/issues/4777#issuecomment-604424845
- const HAS_AUTHOR_SPECIFIED_BORDER_BACKGROUND = 1 << 14;
-
- /// Whether there are author-specified rules for `font-family`.
- const HAS_AUTHOR_SPECIFIED_FONT_FAMILY = 1 << 16;
-
- /// Whether there are author-specified rules for `font-synthesis-weight`.
- const HAS_AUTHOR_SPECIFIED_FONT_SYNTHESIS_WEIGHT = 1 << 17;
-
- /// Whether there are author-specified rules for `font-synthesis-style`.
- const HAS_AUTHOR_SPECIFIED_FONT_SYNTHESIS_STYLE = 1 << 18;
-
- // (There's also font-synthesis-small-caps, but we don't currently need to
- // keep track of that.)
-
- /// Whether there are author-specified rules for `letter-spacing`.
- const HAS_AUTHOR_SPECIFIED_LETTER_SPACING = 1 << 19;
-
- /// Whether there are author-specified rules for `word-spacing`.
- const HAS_AUTHOR_SPECIFIED_WORD_SPACING = 1 << 20;
-
- /// Whether the style depends on viewport units.
- const USES_VIEWPORT_UNITS = 1 << 21;
-
- /// Whether the style depends on viewport units on container queries.
- ///
- /// This needs to be a separate flag from `USES_VIEWPORT_UNITS` because
- /// it causes us to re-match the style (rather than re-cascascading it,
- /// which is enough for other uses of viewport units).
- const USES_VIEWPORT_UNITS_ON_CONTAINER_QUERIES = 1 << 22;
-
- /// A flag used to mark styles which have `container-type` of `size` or
- /// `inline-size`, or under one.
- const SELF_OR_ANCESTOR_HAS_SIZE_CONTAINER_TYPE = 1 << 23;
-
- /// Whether the style evaluated any relative selector.
- const CONSIDERED_RELATIVE_SELECTOR = 1 << 24;
-
- /// Whether the style evaluated the matched element to be an anchor of
- /// a relative selector.
- const ANCHORS_RELATIVE_SELECTOR = 1 << 25;
-
- /// Whether the style uses container query units, in which case the style depends on the
- /// container's size and we can't reuse it across cousins (without double-checking the
- /// container at least).
- const USES_CONTAINER_UNITS = 1 << 26;
- }
-}
-
-impl Default for ComputedValueFlags {
- #[inline]
- fn default() -> Self {
- Self::empty()
- }
-}
-
-impl ComputedValueFlags {
- /// Flags that are unconditionally propagated to descendants.
- #[inline]
- fn inherited_flags() -> Self {
- Self::IS_RELEVANT_LINK_VISITED |
- Self::CAN_BE_FRAGMENTED |
- Self::IS_IN_PSEUDO_ELEMENT_SUBTREE |
- Self::HAS_TEXT_DECORATION_LINES |
- Self::IS_IN_OPACITY_ZERO_SUBTREE |
- Self::SELF_OR_ANCESTOR_HAS_CONTAIN_STYLE |
- Self::SELF_OR_ANCESTOR_HAS_SIZE_CONTAINER_TYPE
- }
-
- /// Flags that may be propagated to descendants.
- #[inline]
- fn maybe_inherited_flags() -> Self {
- Self::inherited_flags() | Self::SHOULD_SUPPRESS_LINEBREAK
- }
-
- /// Flags that are an input to the cascade.
- #[inline]
- fn cascade_input_flags() -> Self {
- Self::USES_VIEWPORT_UNITS_ON_CONTAINER_QUERIES |
- Self::CONSIDERED_RELATIVE_SELECTOR |
- Self::ANCHORS_RELATIVE_SELECTOR
- }
-
- /// Returns the flags that are always propagated to descendants.
- ///
- /// See StyleAdjuster::set_bits and StyleBuilder.
- #[inline]
- pub fn inherited(self) -> Self {
- self & Self::inherited_flags()
- }
-
- /// Flags that are conditionally propagated to descendants, just to handle
- /// properly style invalidation.
- #[inline]
- pub fn maybe_inherited(self) -> Self {
- self & Self::maybe_inherited_flags()
- }
-
- /// Flags that are an input to the cascade.
- #[inline]
- pub fn for_cascade_inputs(self) -> Self {
- self & Self::cascade_input_flags()
- }
-}
diff --git a/components/style/properties/counted_unknown_properties.py b/components/style/properties/counted_unknown_properties.py
deleted file mode 100644
index b1b800812dd..00000000000
--- a/components/style/properties/counted_unknown_properties.py
+++ /dev/null
@@ -1,110 +0,0 @@
-# This Source Code Form is subject to the terms of the Mozilla Public
-# License, v. 2.0. If a copy of the MPL was not distributed with this
-# file, You can obtain one at http://mozilla.org/MPL/2.0/.
-
-COUNTED_UNKNOWN_PROPERTIES = [
- "-webkit-font-smoothing",
- "-webkit-tap-highlight-color",
- "speak",
- "text-size-adjust",
- "-webkit-font-feature-settings",
- "-webkit-user-drag",
- "orphans",
- "widows",
- "-webkit-user-modify",
- "-webkit-margin-before",
- "-webkit-margin-after",
- "-webkit-margin-start",
- "-webkit-column-break-inside",
- "-webkit-padding-start",
- "-webkit-margin-end",
- "-webkit-box-reflect",
- "-webkit-print-color-adjust",
- "-webkit-mask-box-image",
- "-webkit-line-break",
- "alignment-baseline",
- "-webkit-writing-mode",
- "baseline-shift",
- "-webkit-hyphenate-character",
- "-webkit-highlight",
- "background-repeat-x",
- "-webkit-padding-end",
- "background-repeat-y",
- "-webkit-text-emphasis-color",
- "-webkit-margin-top-collapse",
- "-webkit-rtl-ordering",
- "-webkit-padding-before",
- "-webkit-text-decorations-in-effect",
- "-webkit-border-vertical-spacing",
- "-webkit-locale",
- "-webkit-padding-after",
- "-webkit-border-horizontal-spacing",
- "color-rendering",
- "-webkit-column-break-before",
- "-webkit-transform-origin-x",
- "-webkit-transform-origin-y",
- "-webkit-text-emphasis-position",
- "buffered-rendering",
- "-webkit-text-orientation",
- "-webkit-text-combine",
- "-webkit-text-emphasis-style",
- "-webkit-text-emphasis",
- "-webkit-mask-box-image-width",
- "-webkit-mask-box-image-source",
- "-webkit-mask-box-image-outset",
- "-webkit-mask-box-image-slice",
- "-webkit-mask-box-image-repeat",
- "-webkit-margin-after-collapse",
- "-webkit-border-before-color",
- "-webkit-border-before-width",
- "-webkit-perspective-origin-x",
- "-webkit-perspective-origin-y",
- "-webkit-margin-before-collapse",
- "-webkit-border-before-style",
- "-webkit-margin-bottom-collapse",
- "-webkit-ruby-position",
- "-webkit-column-break-after",
- "-webkit-margin-collapse",
- "-webkit-border-before",
- "-webkit-border-end",
- "-webkit-border-after",
- "-webkit-border-start",
- "-webkit-min-logical-width",
- "-webkit-logical-height",
- "-webkit-transform-origin-z",
- "-webkit-font-size-delta",
- "-webkit-logical-width",
- "-webkit-max-logical-width",
- "-webkit-min-logical-height",
- "-webkit-max-logical-height",
- "-webkit-border-end-color",
- "-webkit-border-end-width",
- "-webkit-border-start-color",
- "-webkit-border-start-width",
- "-webkit-border-after-color",
- "-webkit-border-after-width",
- "-webkit-border-end-style",
- "-webkit-border-after-style",
- "-webkit-border-start-style",
- "-webkit-mask-repeat-x",
- "-webkit-mask-repeat-y",
- "user-zoom",
- "min-zoom",
- "-webkit-box-decoration-break",
- "orientation",
- "max-zoom",
- "-webkit-app-region",
- "-webkit-column-rule",
- "-webkit-column-span",
- "-webkit-column-gap",
- "-webkit-shape-outside",
- "-webkit-column-rule-width",
- "-webkit-column-count",
- "-webkit-opacity",
- "-webkit-column-width",
- "-webkit-shape-image-threshold",
- "-webkit-column-rule-style",
- "-webkit-columns",
- "-webkit-column-rule-color",
- "-webkit-shape-margin",
-]
diff --git a/components/style/properties/data.py b/components/style/properties/data.py
deleted file mode 100644
index 03e5b4aa0a6..00000000000
--- a/components/style/properties/data.py
+++ /dev/null
@@ -1,912 +0,0 @@
-# This Source Code Form is subject to the terms of the Mozilla Public
-# License, v. 2.0. If a copy of the MPL was not distributed with this
-# file, You can obtain one at https://mozilla.org/MPL/2.0/.
-
-import re
-from counted_unknown_properties import COUNTED_UNKNOWN_PROPERTIES
-
-PHYSICAL_SIDES = ["top", "right", "bottom", "left"]
-LOGICAL_SIDES = ["block-start", "block-end", "inline-start", "inline-end"]
-PHYSICAL_SIZES = ["width", "height"]
-LOGICAL_SIZES = ["block-size", "inline-size"]
-PHYSICAL_CORNERS = ["top-left", "top-right", "bottom-right", "bottom-left"]
-LOGICAL_CORNERS = ["start-start", "start-end", "end-start", "end-end"]
-PHYSICAL_AXES = ["x", "y"]
-LOGICAL_AXES = ["inline", "block"]
-
-# bool is True when logical
-ALL_SIDES = [(side, False) for side in PHYSICAL_SIDES] + [
- (side, True) for side in LOGICAL_SIDES
-]
-ALL_SIZES = [(size, False) for size in PHYSICAL_SIZES] + [
- (size, True) for size in LOGICAL_SIZES
-]
-ALL_CORNERS = [(corner, False) for corner in PHYSICAL_CORNERS] + [
- (corner, True) for corner in LOGICAL_CORNERS
-]
-ALL_AXES = [(axis, False) for axis in PHYSICAL_AXES] + [
- (axis, True) for axis in LOGICAL_AXES
-]
-
-SYSTEM_FONT_LONGHANDS = """font_family font_size font_style
- font_stretch font_weight""".split()
-
-# Bitfield values for all rule types which can have property declarations.
-STYLE_RULE = 1 << 0
-PAGE_RULE = 1 << 1
-KEYFRAME_RULE = 1 << 2
-
-ALL_RULES = STYLE_RULE | PAGE_RULE | KEYFRAME_RULE
-DEFAULT_RULES = STYLE_RULE | KEYFRAME_RULE
-DEFAULT_RULES_AND_PAGE = DEFAULT_RULES | PAGE_RULE
-DEFAULT_RULES_EXCEPT_KEYFRAME = STYLE_RULE
-
-# Rule name to value dict
-RULE_VALUES = {
- "Style": STYLE_RULE,
- "Page": PAGE_RULE,
- "Keyframe": KEYFRAME_RULE,
-}
-
-
-def rule_values_from_arg(that):
- if isinstance(that, int):
- return that
- mask = 0
- for rule in that.split():
- mask |= RULE_VALUES[rule]
- return mask
-
-
-def maybe_moz_logical_alias(engine, side, prop):
- if engine == "gecko" and side[1]:
- axis, dir = side[0].split("-")
- if axis == "inline":
- return prop % dir
- return None
-
-
-def to_rust_ident(name):
- name = name.replace("-", "_")
- if name in ["static", "super", "box", "move"]: # Rust keywords
- name += "_"
- return name
-
-
-def to_snake_case(ident):
- return re.sub("([A-Z]+)", lambda m: "_" + m.group(1).lower(), ident).strip("_")
-
-
-def to_camel_case(ident):
- return re.sub(
- "(^|_|-)([a-z0-9])", lambda m: m.group(2).upper(), ident.strip("_").strip("-")
- )
-
-
-def to_camel_case_lower(ident):
- camel = to_camel_case(ident)
- return camel[0].lower() + camel[1:]
-
-
-# https://drafts.csswg.org/cssom/#css-property-to-idl-attribute
-def to_idl_name(ident):
- return re.sub("-([a-z])", lambda m: m.group(1).upper(), ident)
-
-
-def parse_aliases(value):
- aliases = {}
- for pair in value.split():
- [a, v] = pair.split("=")
- aliases[a] = v
- return aliases
-
-
-class Keyword(object):
- def __init__(
- self,
- name,
- values,
- gecko_constant_prefix=None,
- gecko_enum_prefix=None,
- custom_consts=None,
- extra_gecko_values=None,
- extra_servo_values=None,
- gecko_aliases=None,
- servo_aliases=None,
- gecko_strip_moz_prefix=None,
- gecko_inexhaustive=None,
- ):
- self.name = name
- self.values = values.split()
- if gecko_constant_prefix and gecko_enum_prefix:
- raise TypeError(
- "Only one of gecko_constant_prefix and gecko_enum_prefix "
- "can be specified"
- )
- self.gecko_constant_prefix = (
- gecko_constant_prefix or "NS_STYLE_" + self.name.upper().replace("-", "_")
- )
- self.gecko_enum_prefix = gecko_enum_prefix
- self.extra_gecko_values = (extra_gecko_values or "").split()
- self.extra_servo_values = (extra_servo_values or "").split()
- self.gecko_aliases = parse_aliases(gecko_aliases or "")
- self.servo_aliases = parse_aliases(servo_aliases or "")
- self.consts_map = {} if custom_consts is None else custom_consts
- self.gecko_strip_moz_prefix = (
- True if gecko_strip_moz_prefix is None else gecko_strip_moz_prefix
- )
- self.gecko_inexhaustive = gecko_inexhaustive or (gecko_enum_prefix is None)
-
- def values_for(self, engine):
- if engine == "gecko":
- return self.values + self.extra_gecko_values
- elif engine == "servo":
- return self.values + self.extra_servo_values
- else:
- raise Exception("Bad engine: " + engine)
-
- def aliases_for(self, engine):
- if engine == "gecko":
- return self.gecko_aliases
- elif engine == "servo":
- return self.servo_aliases
- else:
- raise Exception("Bad engine: " + engine)
-
- def gecko_constant(self, value):
- moz_stripped = (
- value.replace("-moz-", "")
- if self.gecko_strip_moz_prefix
- else value.replace("-moz-", "moz-")
- )
- mapped = self.consts_map.get(value)
- if self.gecko_enum_prefix:
- parts = moz_stripped.replace("-", "_").split("_")
- parts = mapped if mapped else [p.title() for p in parts]
- return self.gecko_enum_prefix + "::" + "".join(parts)
- else:
- suffix = mapped if mapped else moz_stripped.replace("-", "_")
- return self.gecko_constant_prefix + "_" + suffix.upper()
-
- def needs_cast(self):
- return self.gecko_enum_prefix is None
-
- def maybe_cast(self, type_str):
- return "as " + type_str if self.needs_cast() else ""
-
- def casted_constant_name(self, value, cast_type):
- if cast_type is None:
- raise TypeError("We should specify the cast_type.")
-
- if self.gecko_enum_prefix is None:
- return cast_type.upper() + "_" + self.gecko_constant(value)
- else:
- return (
- cast_type.upper()
- + "_"
- + self.gecko_constant(value).upper().replace("::", "_")
- )
-
-
-def arg_to_bool(arg):
- if isinstance(arg, bool):
- return arg
- assert arg in ["True", "False"], "Unexpected value for boolean argument: " + repr(
- arg
- )
- return arg == "True"
-
-
-def parse_property_aliases(alias_list):
- result = []
- if alias_list:
- for alias in alias_list.split():
- (name, _, pref) = alias.partition(":")
- result.append((name, pref))
- return result
-
-
-def to_phys(name, logical, physical):
- return name.replace(logical, physical).replace("inset-", "")
-
-
-class Property(object):
- def __init__(
- self,
- name,
- spec,
- servo_pref,
- gecko_pref,
- enabled_in,
- rule_types_allowed,
- aliases,
- extra_prefixes,
- flags,
- ):
- self.name = name
- if not spec:
- raise TypeError("Spec should be specified for " + name)
- self.spec = spec
- self.ident = to_rust_ident(name)
- self.camel_case = to_camel_case(self.ident)
- self.servo_pref = servo_pref
- self.gecko_pref = gecko_pref
- self.rule_types_allowed = rule_values_from_arg(rule_types_allowed)
- # For enabled_in, the setup is as follows:
- # It needs to be one of the four values: ["", "ua", "chrome", "content"]
- # * "chrome" implies "ua", and implies that they're explicitly
- # enabled.
- # * "" implies the property will never be parsed.
- # * "content" implies the property is accessible unconditionally,
- # modulo a pref, set via servo_pref / gecko_pref.
- assert enabled_in in ("", "ua", "chrome", "content")
- self.enabled_in = enabled_in
- self.aliases = parse_property_aliases(aliases)
- self.extra_prefixes = parse_property_aliases(extra_prefixes)
- self.flags = flags.split() if flags else []
-
- def rule_types_allowed_names(self):
- for name in RULE_VALUES:
- if self.rule_types_allowed & RULE_VALUES[name] != 0:
- yield name
-
- def experimental(self, engine):
- if engine == "gecko":
- return bool(self.gecko_pref)
- elif engine == "servo":
- return bool(self.servo_pref)
- else:
- raise Exception("Bad engine: " + engine)
-
- def explicitly_enabled_in_ua_sheets(self):
- return self.enabled_in in ("ua", "chrome")
-
- def explicitly_enabled_in_chrome(self):
- return self.enabled_in == "chrome"
-
- def enabled_in_content(self):
- return self.enabled_in == "content"
-
- def nscsspropertyid(self):
- return "nsCSSPropertyID::eCSSProperty_" + self.ident
-
-
-class Longhand(Property):
- def __init__(
- self,
- style_struct,
- name,
- spec=None,
- animation_value_type=None,
- keyword=None,
- predefined_type=None,
- servo_pref=None,
- gecko_pref=None,
- enabled_in="content",
- need_index=False,
- gecko_ffi_name=None,
- has_effect_on_gecko_scrollbars=None,
- rule_types_allowed=DEFAULT_RULES,
- cast_type="u8",
- logical=False,
- logical_group=None,
- aliases=None,
- extra_prefixes=None,
- boxed=False,
- flags=None,
- allow_quirks="No",
- ignored_when_colors_disabled=False,
- simple_vector_bindings=False,
- vector=False,
- servo_restyle_damage="repaint",
- ):
- Property.__init__(
- self,
- name=name,
- spec=spec,
- servo_pref=servo_pref,
- gecko_pref=gecko_pref,
- enabled_in=enabled_in,
- rule_types_allowed=rule_types_allowed,
- aliases=aliases,
- extra_prefixes=extra_prefixes,
- flags=flags,
- )
-
- self.keyword = keyword
- self.predefined_type = predefined_type
- self.style_struct = style_struct
- self.has_effect_on_gecko_scrollbars = has_effect_on_gecko_scrollbars
- assert (
- has_effect_on_gecko_scrollbars in [None, False, True]
- and not style_struct.inherited
- or (gecko_pref is None and enabled_in != "")
- == (has_effect_on_gecko_scrollbars is None)
- ), (
- "Property "
- + name
- + ": has_effect_on_gecko_scrollbars must be "
- + "specified, and must have a value of True or False, iff a "
- + "property is inherited and is behind a Gecko pref or internal"
- )
- self.need_index = need_index
- self.gecko_ffi_name = gecko_ffi_name or "m" + self.camel_case
- self.cast_type = cast_type
- self.logical = arg_to_bool(logical)
- self.logical_group = logical_group
- if self.logical:
- assert logical_group, "Property " + name + " must have a logical group"
-
- self.boxed = arg_to_bool(boxed)
- self.allow_quirks = allow_quirks
- self.ignored_when_colors_disabled = ignored_when_colors_disabled
- self.is_vector = vector
- self.simple_vector_bindings = simple_vector_bindings
-
- # This is done like this since just a plain bool argument seemed like
- # really random.
- if animation_value_type is None:
- raise TypeError(
- "animation_value_type should be specified for (" + name + ")"
- )
- self.animation_value_type = animation_value_type
-
- self.animatable = animation_value_type != "none"
- self.transitionable = (
- animation_value_type != "none" and animation_value_type != "discrete"
- )
- self.is_animatable_with_computed_value = (
- animation_value_type == "ComputedValue"
- or animation_value_type == "discrete"
- )
-
- # See compute_damage for the various values this can take
- self.servo_restyle_damage = servo_restyle_damage
-
- @staticmethod
- def type():
- return "longhand"
-
- # For a given logical property return all the physical property names
- # corresponding to it.
- def all_physical_mapped_properties(self, data):
- if not self.logical:
- return []
-
- candidates = [
- s for s in LOGICAL_SIDES + LOGICAL_SIZES + LOGICAL_CORNERS if s in self.name
- ] + [s for s in LOGICAL_AXES if self.name.endswith(s)]
- assert len(candidates) == 1
- logical_side = candidates[0]
-
- physical = (
- PHYSICAL_SIDES
- if logical_side in LOGICAL_SIDES
- else PHYSICAL_SIZES
- if logical_side in LOGICAL_SIZES
- else PHYSICAL_AXES
- if logical_side in LOGICAL_AXES
- else LOGICAL_CORNERS
- )
- return [
- data.longhands_by_name[to_phys(self.name, logical_side, physical_side)]
- for physical_side in physical
- ]
-
- def may_be_disabled_in(self, shorthand, engine):
- if engine == "gecko":
- return self.gecko_pref and self.gecko_pref != shorthand.gecko_pref
- elif engine == "servo":
- return (
- self.servo_pref
- and self.servo_pref != shorthand.servo_pref
- )
- else:
- raise Exception("Bad engine: " + engine)
-
- def base_type(self):
- if self.predefined_type and not self.is_vector:
- return "crate::values::specified::{}".format(self.predefined_type)
- return "longhands::{}::SpecifiedValue".format(self.ident)
-
- def specified_type(self):
- if self.predefined_type and not self.is_vector:
- ty = "crate::values::specified::{}".format(self.predefined_type)
- else:
- ty = "longhands::{}::SpecifiedValue".format(self.ident)
- if self.boxed:
- ty = "Box<{}>".format(ty)
- return ty
-
- def specified_is_copy(self):
- if self.is_vector or self.boxed:
- return False
- if self.predefined_type:
- return self.predefined_type in {
- "AlignContent",
- "AlignItems",
- "AlignSelf",
- "Appearance",
- "AspectRatio",
- "BaselineSource",
- "BreakBetween",
- "BreakWithin",
- "BackgroundRepeat",
- "BorderImageRepeat",
- "BorderStyle",
- "table::CaptionSide",
- "Clear",
- "ColumnCount",
- "Contain",
- "ContentVisibility",
- "ContainerType",
- "Display",
- "FillRule",
- "Float",
- "FontLanguageOverride",
- "FontSizeAdjust",
- "FontStretch",
- "FontStyle",
- "FontSynthesis",
- "FontVariantEastAsian",
- "FontVariantLigatures",
- "FontVariantNumeric",
- "FontWeight",
- "GreaterThanOrEqualToOneNumber",
- "GridAutoFlow",
- "ImageRendering",
- "InitialLetter",
- "Integer",
- "JustifyContent",
- "JustifyItems",
- "JustifySelf",
- "LineBreak",
- "LineClamp",
- "MasonryAutoFlow",
- "BoolInteger",
- "text::MozControlCharacterVisibility",
- "MathDepth",
- "MozScriptMinSize",
- "MozScriptSizeMultiplier",
- "TextDecorationSkipInk",
- "NonNegativeNumber",
- "OffsetRotate",
- "Opacity",
- "OutlineStyle",
- "Overflow",
- "OverflowAnchor",
- "OverflowClipBox",
- "OverflowWrap",
- "OverscrollBehavior",
- "PageOrientation",
- "Percentage",
- "PrintColorAdjust",
- "ForcedColorAdjust",
- "Resize",
- "RubyPosition",
- "SVGOpacity",
- "SVGPaintOrder",
- "ScrollbarGutter",
- "ScrollSnapAlign",
- "ScrollSnapAxis",
- "ScrollSnapStop",
- "ScrollSnapStrictness",
- "ScrollSnapType",
- "TextAlign",
- "TextAlignLast",
- "TextDecorationLine",
- "TextEmphasisPosition",
- "TextJustify",
- "TextTransform",
- "TextUnderlinePosition",
- "TouchAction",
- "TransformStyle",
- "UserSelect",
- "WordBreak",
- "XSpan",
- "XTextScale",
- "ZIndex",
- }
- if self.name == "overflow-y":
- return True
- return bool(self.keyword)
-
- def animated_type(self):
- assert self.animatable
- computed = "<{} as ToComputedValue>::ComputedValue".format(self.base_type())
- if self.is_animatable_with_computed_value:
- return computed
- return "<{} as ToAnimatedValue>::AnimatedValue".format(computed)
-
-
-class Shorthand(Property):
- def __init__(
- self,
- name,
- sub_properties,
- spec=None,
- servo_pref=None,
- gecko_pref=None,
- enabled_in="content",
- rule_types_allowed=DEFAULT_RULES,
- aliases=None,
- extra_prefixes=None,
- flags=None,
- ):
- Property.__init__(
- self,
- name=name,
- spec=spec,
- servo_pref=servo_pref,
- gecko_pref=gecko_pref,
- enabled_in=enabled_in,
- rule_types_allowed=rule_types_allowed,
- aliases=aliases,
- extra_prefixes=extra_prefixes,
- flags=flags,
- )
- self.sub_properties = sub_properties
-
- def get_animatable(self):
- for sub in self.sub_properties:
- if sub.animatable:
- return True
- return False
-
- def get_transitionable(self):
- transitionable = False
- for sub in self.sub_properties:
- if sub.transitionable:
- transitionable = True
- break
- return transitionable
-
- animatable = property(get_animatable)
- transitionable = property(get_transitionable)
-
- @staticmethod
- def type():
- return "shorthand"
-
-
-class Alias(object):
- def __init__(self, name, original, gecko_pref):
- self.name = name
- self.ident = to_rust_ident(name)
- self.camel_case = to_camel_case(self.ident)
- self.original = original
- self.enabled_in = original.enabled_in
- self.animatable = original.animatable
- self.servo_pref = original.servo_pref
- self.gecko_pref = gecko_pref
- self.transitionable = original.transitionable
- self.rule_types_allowed = original.rule_types_allowed
- self.flags = original.flags
-
- @staticmethod
- def type():
- return "alias"
-
- def rule_types_allowed_names(self):
- for name in RULE_VALUES:
- if self.rule_types_allowed & RULE_VALUES[name] != 0:
- yield name
-
- def experimental(self, engine):
- if engine == "gecko":
- return bool(self.gecko_pref)
- elif engine == "servo":
- return bool(self.servo_pref)
- else:
- raise Exception("Bad engine: " + engine)
-
- def explicitly_enabled_in_ua_sheets(self):
- return self.enabled_in in ["ua", "chrome"]
-
- def explicitly_enabled_in_chrome(self):
- return self.enabled_in == "chrome"
-
- def enabled_in_content(self):
- return self.enabled_in == "content"
-
- def nscsspropertyid(self):
- return "nsCSSPropertyID::eCSSPropertyAlias_%s" % self.ident
-
-
-class Method(object):
- def __init__(self, name, return_type=None, arg_types=None, is_mut=False):
- self.name = name
- self.return_type = return_type
- self.arg_types = arg_types or []
- self.is_mut = is_mut
-
- def arg_list(self):
- args = ["_: " + x for x in self.arg_types]
- args = ["&mut self" if self.is_mut else "&self"] + args
- return ", ".join(args)
-
- def signature(self):
- sig = "fn %s(%s)" % (self.name, self.arg_list())
- if self.return_type:
- sig = sig + " -> " + self.return_type
- return sig
-
- def declare(self):
- return self.signature() + ";"
-
- def stub(self):
- return self.signature() + "{ unimplemented!() }"
-
-
-class StyleStruct(object):
- def __init__(self, name, inherited, gecko_name=None, additional_methods=None):
- self.gecko_struct_name = "Gecko" + name
- self.name = name
- self.name_lower = to_snake_case(name)
- self.ident = to_rust_ident(self.name_lower)
- self.longhands = []
- self.inherited = inherited
- self.gecko_name = gecko_name or name
- self.gecko_ffi_name = "nsStyle" + self.gecko_name
- self.additional_methods = additional_methods or []
-
-
-class PropertiesData(object):
- def __init__(self, engine):
- self.engine = engine
- self.style_structs = []
- self.current_style_struct = None
- self.longhands = []
- self.longhands_by_name = {}
- self.longhands_by_logical_group = {}
- self.longhand_aliases = []
- self.shorthands = []
- self.shorthands_by_name = {}
- self.shorthand_aliases = []
- self.counted_unknown_properties = [
- CountedUnknownProperty(p) for p in COUNTED_UNKNOWN_PROPERTIES
- ]
-
- def new_style_struct(self, *args, **kwargs):
- style_struct = StyleStruct(*args, **kwargs)
- self.style_structs.append(style_struct)
- self.current_style_struct = style_struct
-
- def active_style_structs(self):
- return [s for s in self.style_structs if s.additional_methods or s.longhands]
-
- def add_prefixed_aliases(self, property):
- # FIXME Servo's DOM architecture doesn't support vendor-prefixed properties.
- # See servo/servo#14941.
- if self.engine == "gecko":
- for (prefix, pref) in property.extra_prefixes:
- property.aliases.append(("-%s-%s" % (prefix, property.name), pref))
-
- def declare_longhand(self, name, engines=None, **kwargs):
- engines = engines.split()
- if self.engine not in engines:
- return
-
- longhand = Longhand(self.current_style_struct, name, **kwargs)
- self.add_prefixed_aliases(longhand)
- longhand.aliases = [Alias(xp[0], longhand, xp[1]) for xp in longhand.aliases]
- self.longhand_aliases += longhand.aliases
- self.current_style_struct.longhands.append(longhand)
- self.longhands.append(longhand)
- self.longhands_by_name[name] = longhand
- if longhand.logical_group:
- self.longhands_by_logical_group.setdefault(
- longhand.logical_group, []
- ).append(longhand)
-
- return longhand
-
- def declare_shorthand(self, name, sub_properties, engines, *args, **kwargs):
- engines = engines.split()
- if self.engine not in engines:
- return
-
- sub_properties = [self.longhands_by_name[s] for s in sub_properties]
- shorthand = Shorthand(name, sub_properties, *args, **kwargs)
- self.add_prefixed_aliases(shorthand)
- shorthand.aliases = [Alias(xp[0], shorthand, xp[1]) for xp in shorthand.aliases]
- self.shorthand_aliases += shorthand.aliases
- self.shorthands.append(shorthand)
- self.shorthands_by_name[name] = shorthand
- return shorthand
-
- def shorthands_except_all(self):
- return [s for s in self.shorthands if s.name != "all"]
-
- def all_aliases(self):
- return self.longhand_aliases + self.shorthand_aliases
-
-
-def _add_logical_props(data, props):
- groups = set()
- for prop in props:
- if prop not in data.longhands_by_name:
- assert data.engine == "servo"
- continue
- prop = data.longhands_by_name[prop]
- if prop.logical_group:
- groups.add(prop.logical_group)
- for group in groups:
- for prop in data.longhands_by_logical_group[group]:
- props.add(prop.name)
-
-
-# These are probably Gecko bugs and should be supported per spec.
-def _remove_common_first_line_and_first_letter_properties(props, engine):
- if engine == "gecko":
- props.remove("tab-size")
- props.remove("hyphens")
- props.remove("line-break")
- props.remove("text-align-last")
- props.remove("text-emphasis-position")
- props.remove("text-emphasis-style")
- props.remove("text-emphasis-color")
-
- props.remove("overflow-wrap")
- props.remove("text-align")
- props.remove("text-justify")
- props.remove("white-space")
- props.remove("word-break")
- props.remove("text-indent")
-
-
-class PropertyRestrictions:
- @staticmethod
- def logical_group(data, group):
- return [p.name for p in data.longhands_by_logical_group[group]]
-
- @staticmethod
- def shorthand(data, shorthand):
- if shorthand not in data.shorthands_by_name:
- return []
- return [p.name for p in data.shorthands_by_name[shorthand].sub_properties]
-
- @staticmethod
- def spec(data, spec_path):
- return [p.name for p in data.longhands if spec_path in p.spec]
-
- # https://drafts.csswg.org/css-pseudo/#first-letter-styling
- @staticmethod
- def first_letter(data):
- props = set(
- [
- "color",
- "opacity",
- "float",
- "initial-letter",
- # Kinda like css-fonts?
- "-moz-osx-font-smoothing",
- # Kinda like css-text?
- "-webkit-text-stroke-width",
- "-webkit-text-fill-color",
- "-webkit-text-stroke-color",
- "vertical-align",
- # Will become shorthand of vertical-align (Bug 1830771)
- "baseline-source",
- "line-height",
- # Kinda like css-backgrounds?
- "background-blend-mode",
- ]
- + PropertyRestrictions.shorthand(data, "padding")
- + PropertyRestrictions.shorthand(data, "margin")
- + PropertyRestrictions.spec(data, "css-fonts")
- + PropertyRestrictions.spec(data, "css-backgrounds")
- + PropertyRestrictions.spec(data, "css-text")
- + PropertyRestrictions.spec(data, "css-shapes")
- + PropertyRestrictions.spec(data, "css-text-decor")
- )
-
- _add_logical_props(data, props)
-
- _remove_common_first_line_and_first_letter_properties(props, data.engine)
- return props
-
- # https://drafts.csswg.org/css-pseudo/#first-line-styling
- @staticmethod
- def first_line(data):
- props = set(
- [
- # Per spec.
- "color",
- "opacity",
- # Kinda like css-fonts?
- "-moz-osx-font-smoothing",
- # Kinda like css-text?
- "-webkit-text-stroke-width",
- "-webkit-text-fill-color",
- "-webkit-text-stroke-color",
- "vertical-align",
- # Will become shorthand of vertical-align (Bug 1830771)
- "baseline-source",
- "line-height",
- # Kinda like css-backgrounds?
- "background-blend-mode",
- ]
- + PropertyRestrictions.spec(data, "css-fonts")
- + PropertyRestrictions.spec(data, "css-backgrounds")
- + PropertyRestrictions.spec(data, "css-text")
- + PropertyRestrictions.spec(data, "css-text-decor")
- )
-
- # These are probably Gecko bugs and should be supported per spec.
- for prop in PropertyRestrictions.shorthand(data, "border"):
- props.remove(prop)
- for prop in PropertyRestrictions.shorthand(data, "border-radius"):
- props.remove(prop)
- props.remove("box-shadow")
-
- _remove_common_first_line_and_first_letter_properties(props, data.engine)
- return props
-
- # https://drafts.csswg.org/css-pseudo/#placeholder
- #
- # The spec says that placeholder and first-line have the same restrictions,
- # but that's not true in Gecko and we also allow a handful other properties
- # for ::placeholder.
- @staticmethod
- def placeholder(data):
- props = PropertyRestrictions.first_line(data)
- props.add("opacity")
- props.add("white-space")
- props.add("text-overflow")
- props.add("text-align")
- props.add("text-justify")
- return props
-
- # https://drafts.csswg.org/css-pseudo/#marker-pseudo
- @staticmethod
- def marker(data):
- return set(
- [
- "white-space",
- "color",
- "text-combine-upright",
- "text-transform",
- "unicode-bidi",
- "direction",
- "content",
- "line-height",
- "-moz-osx-font-smoothing",
- ]
- + PropertyRestrictions.spec(data, "css-fonts")
- + PropertyRestrictions.spec(data, "css-animations")
- + PropertyRestrictions.spec(data, "css-transitions")
- )
-
- # https://www.w3.org/TR/webvtt1/#the-cue-pseudo-element
- @staticmethod
- def cue(data):
- return set(
- [
- "color",
- "opacity",
- "visibility",
- "text-shadow",
- "white-space",
- "text-combine-upright",
- "ruby-position",
- # XXX Should these really apply to cue?
- "-moz-osx-font-smoothing",
- # FIXME(emilio): background-blend-mode should be part of the
- # background shorthand, and get reset, per
- # https://drafts.fxtf.org/compositing/#background-blend-mode
- "background-blend-mode",
- ]
- + PropertyRestrictions.shorthand(data, "text-decoration")
- + PropertyRestrictions.shorthand(data, "background")
- + PropertyRestrictions.shorthand(data, "outline")
- + PropertyRestrictions.shorthand(data, "font")
- + PropertyRestrictions.shorthand(data, "font-synthesis")
- )
-
-
-class CountedUnknownProperty:
- def __init__(self, name):
- self.name = name
- self.ident = to_rust_ident(name)
- self.camel_case = to_camel_case(self.ident)
diff --git a/components/style/properties/declaration_block.rs b/components/style/properties/declaration_block.rs
deleted file mode 100644
index 90845af311a..00000000000
--- a/components/style/properties/declaration_block.rs
+++ /dev/null
@@ -1,1613 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
-
-//! A property declaration block.
-
-#![deny(missing_docs)]
-
-use super::generated::{
- shorthands, AllShorthand, ComputedValues, LogicalGroupSet, LonghandIdSet,
- NonCustomPropertyIdSet, PropertyDeclaration, PropertyDeclarationId, PropertyId, ShorthandId,
- SourcePropertyDeclaration, SourcePropertyDeclarationDrain, SubpropertiesVec,
-};
-use crate::applicable_declarations::CascadePriority;
-use crate::context::QuirksMode;
-use crate::custom_properties::{self, CustomPropertiesBuilder};
-use crate::error_reporting::{ContextualParseError, ParseErrorReporter};
-use crate::media_queries::Device;
-use crate::parser::ParserContext;
-use crate::properties::animated_properties::{AnimationValue, AnimationValueMap};
-use crate::rule_tree::CascadeLevel;
-use crate::selector_map::PrecomputedHashSet;
-use crate::selector_parser::SelectorImpl;
-use crate::shared_lock::Locked;
-use crate::str::{CssString, CssStringWriter};
-use crate::stylesheets::{layer_rule::LayerOrder, CssRuleType, Origin, UrlExtraData};
-use crate::values::computed::Context;
-use cssparser::{
- parse_important, AtRuleParser, CowRcStr, DeclarationParser, Delimiter, ParseErrorKind, Parser,
- ParserInput, QualifiedRuleParser, RuleBodyItemParser, RuleBodyParser,
-};
-use itertools::Itertools;
-use selectors::SelectorList;
-use servo_arc::Arc;
-use smallbitvec::{self, SmallBitVec};
-use smallvec::SmallVec;
-use std::fmt::{self, Write};
-use std::iter::{DoubleEndedIterator, Zip};
-use std::slice::Iter;
-use style_traits::{CssWriter, ParseError, ParsingMode, StyleParseErrorKind, ToCss};
-use thin_vec::ThinVec;
-
-/// A set of property declarations including animations and transitions.
-#[derive(Default)]
-pub struct AnimationDeclarations {
- /// Declarations for animations.
- pub animations: Option<Arc<Locked<PropertyDeclarationBlock>>>,
- /// Declarations for transitions.
- pub transitions: Option<Arc<Locked<PropertyDeclarationBlock>>>,
-}
-
-impl AnimationDeclarations {
- /// Whether or not this `AnimationDeclarations` is empty.
- pub fn is_empty(&self) -> bool {
- self.animations.is_none() && self.transitions.is_none()
- }
-}
-
-/// An enum describes how a declaration should update
-/// the declaration block.
-#[derive(Clone, Copy, Debug, Eq, PartialEq)]
-enum DeclarationUpdate {
- /// The given declaration doesn't update anything.
- None,
- /// The given declaration is new, and should be append directly.
- Append,
- /// The given declaration can be updated in-place at the given position.
- UpdateInPlace { pos: usize },
- /// The given declaration cannot be updated in-place, and an existing
- /// one needs to be removed at the given position.
- AppendAndRemove { pos: usize },
-}
-
-/// A struct describes how a declaration block should be updated by
-/// a `SourcePropertyDeclaration`.
-#[derive(Default)]
-pub struct SourcePropertyDeclarationUpdate {
- updates: SubpropertiesVec<DeclarationUpdate>,
- new_count: usize,
- any_removal: bool,
-}
-
-/// A declaration [importance][importance].
-///
-/// [importance]: https://drafts.csswg.org/css-cascade/#importance
-#[derive(Clone, Copy, Debug, Eq, Hash, MallocSizeOf, PartialEq)]
-pub enum Importance {
- /// Indicates a declaration without `!important`.
- Normal,
-
- /// Indicates a declaration with `!important`.
- Important,
-}
-
-impl Default for Importance {
- fn default() -> Self {
- Self::Normal
- }
-}
-
-impl Importance {
- /// Return whether this is an important declaration.
- pub fn important(self) -> bool {
- match self {
- Self::Normal => false,
- Self::Important => true,
- }
- }
-}
-
-#[derive(Clone, ToShmem, Default, MallocSizeOf)]
-struct PropertyDeclarationIdSet {
- longhands: LonghandIdSet,
- custom: PrecomputedHashSet<custom_properties::Name>,
-}
-
-impl PropertyDeclarationIdSet {
- fn insert(&mut self, id: PropertyDeclarationId) -> bool {
- match id {
- PropertyDeclarationId::Longhand(id) => {
- if self.longhands.contains(id) {
- return false;
- }
- self.longhands.insert(id);
- return true;
- },
- PropertyDeclarationId::Custom(name) => self.custom.insert(name.clone()),
- }
- }
-
- fn contains(&self, id: PropertyDeclarationId) -> bool {
- match id {
- PropertyDeclarationId::Longhand(id) => self.longhands.contains(id),
- PropertyDeclarationId::Custom(name) => self.custom.contains(name),
- }
- }
-
- fn remove(&mut self, id: PropertyDeclarationId) {
- match id {
- PropertyDeclarationId::Longhand(id) => self.longhands.remove(id),
- PropertyDeclarationId::Custom(name) => {
- self.custom.remove(name);
- },
- }
- }
-
- fn clear(&mut self) {
- self.longhands.clear();
- self.custom.clear();
- }
-}
-
-/// Overridden declarations are skipped.
-#[cfg_attr(feature = "gecko", derive(MallocSizeOf))]
-#[derive(Clone, ToShmem, Default)]
-pub struct PropertyDeclarationBlock {
- /// The group of declarations, along with their importance.
- ///
- /// Only deduplicated declarations appear here.
- declarations: ThinVec<PropertyDeclaration>,
-
- /// The "important" flag for each declaration in `declarations`.
- declarations_importance: SmallBitVec,
-
- /// The set of properties that are present in the block.
- property_ids: PropertyDeclarationIdSet,
-}
-
-/// Iterator over `(PropertyDeclaration, Importance)` pairs.
-pub struct DeclarationImportanceIterator<'a> {
- iter: Zip<Iter<'a, PropertyDeclaration>, smallbitvec::Iter<'a>>,
-}
-
-impl<'a> Default for DeclarationImportanceIterator<'a> {
- fn default() -> Self {
- Self {
- iter: [].iter().zip(smallbitvec::Iter::default()),
- }
- }
-}
-
-impl<'a> DeclarationImportanceIterator<'a> {
- /// Constructor.
- fn new(declarations: &'a [PropertyDeclaration], important: &'a SmallBitVec) -> Self {
- DeclarationImportanceIterator {
- iter: declarations.iter().zip(important.iter()),
- }
- }
-}
-
-impl<'a> Iterator for DeclarationImportanceIterator<'a> {
- type Item = (&'a PropertyDeclaration, Importance);
-
- #[inline]
- fn next(&mut self) -> Option<Self::Item> {
- self.iter.next().map(|(decl, important)| {
- (
- decl,
- if important {
- Importance::Important
- } else {
- Importance::Normal
- },
- )
- })
- }
-
- #[inline]
- fn size_hint(&self) -> (usize, Option<usize>) {
- self.iter.size_hint()
- }
-}
-
-impl<'a> DoubleEndedIterator for DeclarationImportanceIterator<'a> {
- #[inline(always)]
- fn next_back(&mut self) -> Option<Self::Item> {
- self.iter.next_back().map(|(decl, important)| {
- (
- decl,
- if important {
- Importance::Important
- } else {
- Importance::Normal
- },
- )
- })
- }
-}
-
-/// Iterator for AnimationValue to be generated from PropertyDeclarationBlock.
-pub struct AnimationValueIterator<'a, 'cx, 'cx_a: 'cx> {
- iter: DeclarationImportanceIterator<'a>,
- context: &'cx mut Context<'cx_a>,
- default_values: &'a ComputedValues,
- /// Custom properties in a keyframe if exists.
- extra_custom_properties: Option<&'a Arc<crate::custom_properties::CustomPropertiesMap>>,
-}
-
-impl<'a, 'cx, 'cx_a: 'cx> AnimationValueIterator<'a, 'cx, 'cx_a> {
- fn new(
- declarations: &'a PropertyDeclarationBlock,
- context: &'cx mut Context<'cx_a>,
- default_values: &'a ComputedValues,
- extra_custom_properties: Option<&'a Arc<crate::custom_properties::CustomPropertiesMap>>,
- ) -> AnimationValueIterator<'a, 'cx, 'cx_a> {
- AnimationValueIterator {
- iter: declarations.declaration_importance_iter(),
- context,
- default_values,
- extra_custom_properties,
- }
- }
-}
-
-impl<'a, 'cx, 'cx_a: 'cx> Iterator for AnimationValueIterator<'a, 'cx, 'cx_a> {
- type Item = AnimationValue;
- #[inline]
- fn next(&mut self) -> Option<Self::Item> {
- loop {
- let (decl, importance) = self.iter.next()?;
-
- if importance.important() {
- continue;
- }
-
- let animation = AnimationValue::from_declaration(
- decl,
- &mut self.context,
- self.extra_custom_properties,
- self.default_values,
- );
-
- if let Some(anim) = animation {
- return Some(anim);
- }
- }
- }
-}
-
-impl fmt::Debug for PropertyDeclarationBlock {
- fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
- self.declarations.fmt(f)
- }
-}
-
-impl PropertyDeclarationBlock {
- /// Returns the number of declarations in the block.
- #[inline]
- pub fn len(&self) -> usize {
- self.declarations.len()
- }
-
- /// Returns whether the block is empty.
- #[inline]
- pub fn is_empty(&self) -> bool {
- self.declarations.is_empty()
- }
-
- /// Create an empty block
- #[inline]
- pub fn new() -> Self {
- PropertyDeclarationBlock {
- declarations: ThinVec::new(),
- declarations_importance: SmallBitVec::new(),
- property_ids: PropertyDeclarationIdSet::default(),
- }
- }
-
- /// Create a block with a single declaration
- pub fn with_one(declaration: PropertyDeclaration, importance: Importance) -> Self {
- let mut property_ids = PropertyDeclarationIdSet::default();
- property_ids.insert(declaration.id());
- let mut declarations = ThinVec::with_capacity(1);
- declarations.push(declaration);
- PropertyDeclarationBlock {
- declarations,
- declarations_importance: SmallBitVec::from_elem(1, importance.important()),
- property_ids,
- }
- }
-
- /// The declarations in this block
- #[inline]
- pub fn declarations(&self) -> &[PropertyDeclaration] {
- &self.declarations
- }
-
- /// The `important` flags for declarations in this block
- #[inline]
- pub fn declarations_importance(&self) -> &SmallBitVec {
- &self.declarations_importance
- }
-
- /// Iterate over `(PropertyDeclaration, Importance)` pairs
- #[inline]
- pub fn declaration_importance_iter(&self) -> DeclarationImportanceIterator {
- DeclarationImportanceIterator::new(&self.declarations, &self.declarations_importance)
- }
-
- /// Iterate over `PropertyDeclaration` for Importance::Normal
- #[inline]
- pub fn normal_declaration_iter<'a>(
- &'a self,
- ) -> impl DoubleEndedIterator<Item = &'a PropertyDeclaration> {
- self.declaration_importance_iter()
- .filter(|(_, importance)| !importance.important())
- .map(|(declaration, _)| declaration)
- }
-
- /// Return an iterator of (AnimatableLonghand, AnimationValue).
- #[inline]
- pub fn to_animation_value_iter<'a, 'cx, 'cx_a: 'cx>(
- &'a self,
- context: &'cx mut Context<'cx_a>,
- default_values: &'a ComputedValues,
- extra_custom_properties: Option<&'a Arc<crate::custom_properties::CustomPropertiesMap>>,
- ) -> AnimationValueIterator<'a, 'cx, 'cx_a> {
- AnimationValueIterator::new(self, context, default_values, extra_custom_properties)
- }
-
- /// Returns whether this block contains any declaration with `!important`.
- ///
- /// This is based on the `declarations_importance` bit-vector,
- /// which should be maintained whenever `declarations` is changed.
- #[inline]
- pub fn any_important(&self) -> bool {
- !self.declarations_importance.all_false()
- }
-
- /// Returns whether this block contains any declaration without `!important`.
- ///
- /// This is based on the `declarations_importance` bit-vector,
- /// which should be maintained whenever `declarations` is changed.
- #[inline]
- pub fn any_normal(&self) -> bool {
- !self.declarations_importance.all_true()
- }
-
- /// Returns a `LonghandIdSet` representing the properties that are changed in
- /// this block.
- #[inline]
- pub fn longhands(&self) -> &LonghandIdSet {
- &self.property_ids.longhands
- }
-
- /// Returns whether this block contains a declaration of a given property id.
- #[inline]
- pub fn contains(&self, id: PropertyDeclarationId) -> bool {
- self.property_ids.contains(id)
- }
-
- /// Returns whether this block contains any reset longhand.
- #[inline]
- pub fn contains_any_reset(&self) -> bool {
- self.property_ids.longhands.contains_any_reset()
- }
-
- /// Get a declaration for a given property.
- ///
- /// NOTE: This is linear time in the case of custom properties or in the
- /// case the longhand is actually in the declaration block.
- #[inline]
- pub fn get(
- &self,
- property: PropertyDeclarationId,
- ) -> Option<(&PropertyDeclaration, Importance)> {
- if !self.contains(property) {
- return None;
- }
- self.declaration_importance_iter()
- .find(|(declaration, _)| declaration.id() == property)
- }
-
- /// Tries to serialize a given shorthand from the declarations in this
- /// block.
- pub fn shorthand_to_css(
- &self,
- shorthand: ShorthandId,
- dest: &mut CssStringWriter,
- ) -> fmt::Result {
- // Step 1.2.1 of
- // https://drafts.csswg.org/cssom/#dom-cssstyledeclaration-getpropertyvalue
- let mut list = SmallVec::<[&_; 10]>::new();
- let mut important_count = 0;
-
- // Step 1.2.2
- for longhand in shorthand.longhands() {
- // Step 1.2.2.1
- let declaration = self.get(PropertyDeclarationId::Longhand(longhand));
-
- // Step 1.2.2.2 & 1.2.2.3
- match declaration {
- Some((declaration, importance)) => {
- list.push(declaration);
- if importance.important() {
- important_count += 1;
- }
- },
- None => return Ok(()),
- }
- }
-
- // If there is one or more longhand with important, and one or more
- // without important, we don't serialize it as a shorthand.
- if important_count > 0 && important_count != list.len() {
- return Ok(());
- }
-
- // Step 1.2.3
- // We don't print !important when serializing individual properties,
- // so we treat this as a normal-importance property
- match shorthand.get_shorthand_appendable_value(&list) {
- Some(appendable_value) => append_declaration_value(dest, appendable_value),
- None => return Ok(()),
- }
- }
-
- /// Find the value of the given property in this block and serialize it
- ///
- /// <https://drafts.csswg.org/cssom/#dom-cssstyledeclaration-getpropertyvalue>
- pub fn property_value_to_css(
- &self,
- property: &PropertyId,
- dest: &mut CssStringWriter,
- ) -> fmt::Result {
- // Step 1.1: done when parsing a string to PropertyId
-
- // Step 1.2
- let longhand_or_custom = match property.as_shorthand() {
- Ok(shorthand) => return self.shorthand_to_css(shorthand, dest),
- Err(longhand_or_custom) => longhand_or_custom,
- };
-
- if let Some((value, _importance)) = self.get(longhand_or_custom) {
- // Step 2
- value.to_css(dest)
- } else {
- // Step 3
- Ok(())
- }
- }
-
- /// <https://drafts.csswg.org/cssom/#dom-cssstyledeclaration-getpropertypriority>
- pub fn property_priority(&self, property: &PropertyId) -> Importance {
- // Step 1: done when parsing a string to PropertyId
-
- // Step 2
- match property.as_shorthand() {
- Ok(shorthand) => {
- // Step 2.1 & 2.2 & 2.3
- if shorthand.longhands().all(|l| {
- self.get(PropertyDeclarationId::Longhand(l))
- .map_or(false, |(_, importance)| importance.important())
- }) {
- Importance::Important
- } else {
- Importance::Normal
- }
- },
- Err(longhand_or_custom) => {
- // Step 3
- self.get(longhand_or_custom)
- .map_or(Importance::Normal, |(_, importance)| importance)
- },
- }
- }
-
- /// Adds or overrides the declaration for a given property in this block.
- ///
- /// See the documentation of `push` to see what impact `source` has when the
- /// property is already there.
- pub fn extend(
- &mut self,
- mut drain: SourcePropertyDeclarationDrain,
- importance: Importance,
- ) -> bool {
- let all_shorthand_len = match drain.all_shorthand {
- AllShorthand::NotSet => 0,
- AllShorthand::CSSWideKeyword(_) | AllShorthand::WithVariables(_) => {
- shorthands::ALL_SHORTHAND_MAX_LEN
- },
- };
- let push_calls_count = drain.declarations.len() + all_shorthand_len;
-
- // With deduplication the actual length increase may be less than this.
- self.declarations.reserve(push_calls_count);
-
- let mut changed = false;
- for decl in &mut drain.declarations {
- changed |= self.push(decl, importance);
- }
- drain
- .all_shorthand
- .declarations()
- .fold(changed, |changed, decl| {
- changed | self.push(decl, importance)
- })
- }
-
- /// Adds or overrides the declaration for a given property in this block.
- ///
- /// Returns whether the declaration has changed.
- ///
- /// This is only used for parsing and internal use.
- pub fn push(&mut self, declaration: PropertyDeclaration, importance: Importance) -> bool {
- let id = declaration.id();
- if !self.property_ids.insert(id) {
- let mut index_to_remove = None;
- for (i, slot) in self.declarations.iter_mut().enumerate() {
- if slot.id() != id {
- continue;
- }
-
- let important = self.declarations_importance[i];
-
- // For declarations from parsing, non-important declarations
- // shouldn't override existing important one.
- if important && !importance.important() {
- return false;
- }
-
- index_to_remove = Some(i);
- break;
- }
-
- if let Some(index) = index_to_remove {
- self.declarations.remove(index);
- self.declarations_importance.remove(index);
- self.declarations.push(declaration);
- self.declarations_importance.push(importance.important());
- return true;
- }
- }
-
- self.declarations.push(declaration);
- self.declarations_importance.push(importance.important());
- true
- }
-
- /// Prepares updating this declaration block with the given
- /// `SourcePropertyDeclaration` and importance, and returns whether
- /// there is something to update.
- pub fn prepare_for_update(
- &self,
- source_declarations: &SourcePropertyDeclaration,
- importance: Importance,
- updates: &mut SourcePropertyDeclarationUpdate,
- ) -> bool {
- debug_assert!(updates.updates.is_empty());
- // Check whether we are updating for an all shorthand change.
- if !matches!(source_declarations.all_shorthand, AllShorthand::NotSet) {
- debug_assert!(source_declarations.declarations.is_empty());
- return source_declarations
- .all_shorthand
- .declarations()
- .any(|decl| {
- !self.contains(decl.id()) ||
- self.declarations
- .iter()
- .enumerate()
- .find(|&(_, ref d)| d.id() == decl.id())
- .map_or(true, |(i, d)| {
- let important = self.declarations_importance[i];
- *d != decl || important != importance.important()
- })
- });
- }
- // Fill `updates` with update information.
- let mut any_update = false;
- let new_count = &mut updates.new_count;
- let any_removal = &mut updates.any_removal;
- let updates = &mut updates.updates;
- updates.extend(
- source_declarations
- .declarations
- .iter()
- .map(|declaration| {
- if !self.contains(declaration.id()) {
- return DeclarationUpdate::Append;
- }
- let longhand_id = declaration.id().as_longhand();
- if let Some(longhand_id) = longhand_id {
- if let Some(logical_group) = longhand_id.logical_group() {
- let mut needs_append = false;
- for (pos, decl) in self.declarations.iter().enumerate().rev() {
- let id = match decl.id().as_longhand() {
- Some(id) => id,
- None => continue,
- };
- if id == longhand_id {
- if needs_append {
- return DeclarationUpdate::AppendAndRemove { pos };
- }
- let important = self.declarations_importance[pos];
- if decl == declaration && important == importance.important() {
- return DeclarationUpdate::None;
- }
- return DeclarationUpdate::UpdateInPlace { pos };
- }
- if !needs_append &&
- id.logical_group() == Some(logical_group) &&
- id.is_logical() != longhand_id.is_logical()
- {
- needs_append = true;
- }
- }
- unreachable!("Longhand should be found in loop above");
- }
- }
- self.declarations
- .iter()
- .enumerate()
- .find(|&(_, ref decl)| decl.id() == declaration.id())
- .map_or(DeclarationUpdate::Append, |(pos, decl)| {
- let important = self.declarations_importance[pos];
- if decl == declaration && important == importance.important() {
- DeclarationUpdate::None
- } else {
- DeclarationUpdate::UpdateInPlace { pos }
- }
- })
- })
- .inspect(|update| {
- if matches!(update, DeclarationUpdate::None) {
- return;
- }
- any_update = true;
- match update {
- DeclarationUpdate::Append => {
- *new_count += 1;
- },
- DeclarationUpdate::AppendAndRemove { .. } => {
- *any_removal = true;
- },
- _ => {},
- }
- }),
- );
- any_update
- }
-
- /// Update this declaration block with the given data.
- pub fn update(
- &mut self,
- drain: SourcePropertyDeclarationDrain,
- importance: Importance,
- updates: &mut SourcePropertyDeclarationUpdate,
- ) {
- let important = importance.important();
- if !matches!(drain.all_shorthand, AllShorthand::NotSet) {
- debug_assert!(updates.updates.is_empty());
- for decl in drain.all_shorthand.declarations() {
- let id = decl.id();
- if self.property_ids.insert(id) {
- self.declarations.push(decl);
- self.declarations_importance.push(important);
- } else {
- let (idx, slot) = self
- .declarations
- .iter_mut()
- .enumerate()
- .find(|&(_, ref d)| d.id() == decl.id())
- .unwrap();
- *slot = decl;
- self.declarations_importance.set(idx, important);
- }
- }
- return;
- }
-
- self.declarations.reserve(updates.new_count);
- if updates.any_removal {
- // Prepare for removal and fixing update positions.
- struct UpdateOrRemoval<'a> {
- item: &'a mut DeclarationUpdate,
- pos: usize,
- remove: bool,
- }
- let mut updates_and_removals: SubpropertiesVec<UpdateOrRemoval> = updates
- .updates
- .iter_mut()
- .filter_map(|item| {
- let (pos, remove) = match *item {
- DeclarationUpdate::UpdateInPlace { pos } => (pos, false),
- DeclarationUpdate::AppendAndRemove { pos } => (pos, true),
- _ => return None,
- };
- Some(UpdateOrRemoval { item, pos, remove })
- })
- .collect();
- // Execute removals. It's important to do it in reverse index order,
- // so that removing doesn't invalidate following positions.
- updates_and_removals.sort_unstable_by_key(|update| update.pos);
- updates_and_removals
- .iter()
- .rev()
- .filter(|update| update.remove)
- .for_each(|update| {
- self.declarations.remove(update.pos);
- self.declarations_importance.remove(update.pos);
- });
- // Fixup pos field for updates.
- let mut removed_count = 0;
- for update in updates_and_removals.iter_mut() {
- if update.remove {
- removed_count += 1;
- continue;
- }
- debug_assert_eq!(
- *update.item,
- DeclarationUpdate::UpdateInPlace { pos: update.pos }
- );
- *update.item = DeclarationUpdate::UpdateInPlace {
- pos: update.pos - removed_count,
- };
- }
- }
- // Execute updates and appends.
- for (decl, update) in drain.declarations.zip_eq(updates.updates.iter()) {
- match *update {
- DeclarationUpdate::None => {},
- DeclarationUpdate::Append | DeclarationUpdate::AppendAndRemove { .. } => {
- self.property_ids.insert(decl.id());
- self.declarations.push(decl);
- self.declarations_importance.push(important);
- },
- DeclarationUpdate::UpdateInPlace { pos } => {
- self.declarations[pos] = decl;
- self.declarations_importance.set(pos, important);
- },
- }
- }
- updates.updates.clear();
- }
-
- /// Returns the first declaration that would be removed by removing
- /// `property`.
- #[inline]
- pub fn first_declaration_to_remove(&self, property: &PropertyId) -> Option<usize> {
- if let Err(longhand_or_custom) = property.as_shorthand() {
- if !self.contains(longhand_or_custom) {
- return None;
- }
- }
-
- self.declarations
- .iter()
- .position(|declaration| declaration.id().is_or_is_longhand_of(property))
- }
-
- /// Removes a given declaration at a given index.
- #[inline]
- fn remove_declaration_at(&mut self, i: usize) {
- self.property_ids.remove(self.declarations[i].id());
- self.declarations_importance.remove(i);
- self.declarations.remove(i);
- }
-
- /// Clears all the declarations from this block.
- #[inline]
- pub fn clear(&mut self) {
- self.declarations_importance.clear();
- self.declarations.clear();
- self.property_ids.clear();
- }
-
- /// <https://drafts.csswg.org/cssom/#dom-cssstyledeclaration-removeproperty>
- ///
- /// `first_declaration` needs to be the result of
- /// `first_declaration_to_remove`.
- #[inline]
- pub fn remove_property(&mut self, property: &PropertyId, first_declaration: usize) {
- debug_assert_eq!(
- Some(first_declaration),
- self.first_declaration_to_remove(property)
- );
- debug_assert!(self.declarations[first_declaration]
- .id()
- .is_or_is_longhand_of(property));
-
- self.remove_declaration_at(first_declaration);
-
- let shorthand = match property.as_shorthand() {
- Ok(s) => s,
- Err(_longhand_or_custom) => return,
- };
-
- let mut i = first_declaration;
- let mut len = self.len();
- while i < len {
- if !self.declarations[i].id().is_longhand_of(shorthand) {
- i += 1;
- continue;
- }
-
- self.remove_declaration_at(i);
- len -= 1;
- }
- }
-
- /// Take a declaration block known to contain a single property and serialize it.
- pub fn single_value_to_css(
- &self,
- property: &PropertyId,
- dest: &mut CssStringWriter,
- computed_values: Option<&ComputedValues>,
- custom_properties_block: Option<&PropertyDeclarationBlock>,
- device: &Device,
- ) -> fmt::Result {
- if let Ok(shorthand) = property.as_shorthand() {
- return self.shorthand_to_css(shorthand, dest);
- }
-
- // FIXME(emilio): Should this assert, or assert that the declaration is
- // the property we expect?
- let declaration = match self.declarations.get(0) {
- Some(d) => d,
- None => return Err(fmt::Error),
- };
-
- let custom_properties = if let Some(cv) = computed_values {
- // If there are extra custom properties for this declaration block,
- // factor them in too.
- if let Some(block) = custom_properties_block {
- // FIXME(emilio): This is not super-efficient here, and all this
- // feels like a hack anyway...
- block.cascade_custom_properties(cv.custom_properties(), device)
- } else {
- cv.custom_properties().cloned()
- }
- } else {
- None
- };
-
- match (declaration, computed_values) {
- // If we have a longhand declaration with variables, those variables
- // will be stored as unparsed values.
- //
- // As a temporary measure to produce sensible results in Gecko's
- // getKeyframes() implementation for CSS animations, if
- // |computed_values| is supplied, we use it to expand such variable
- // declarations. This will be fixed properly in Gecko bug 1391537.
- (&PropertyDeclaration::WithVariables(ref declaration), Some(ref computed_values)) => {
- declaration
- .value
- .substitute_variables(
- declaration.id,
- computed_values.writing_mode,
- custom_properties.as_ref(),
- QuirksMode::NoQuirks,
- device,
- &mut Default::default(),
- )
- .to_css(dest)
- },
- (ref d, _) => d.to_css(dest),
- }
- }
-
- /// Convert AnimationValueMap to PropertyDeclarationBlock.
- pub fn from_animation_value_map(animation_value_map: &AnimationValueMap) -> Self {
- let len = animation_value_map.len();
- let mut declarations = ThinVec::with_capacity(len);
- let mut property_ids = PropertyDeclarationIdSet::default();
-
- for (property, animation_value) in animation_value_map.iter() {
- property_ids.longhands.insert(*property);
- declarations.push(animation_value.uncompute());
- }
-
- PropertyDeclarationBlock {
- declarations,
- property_ids,
- declarations_importance: SmallBitVec::from_elem(len, false),
- }
- }
-
- /// Returns true if the declaration block has a CSSWideKeyword for the given
- /// property.
- pub fn has_css_wide_keyword(&self, property: &PropertyId) -> bool {
- if let Err(longhand_or_custom) = property.as_shorthand() {
- if !self.property_ids.contains(longhand_or_custom) {
- return false;
- }
- }
- self.declarations.iter().any(|decl| {
- decl.id().is_or_is_longhand_of(property) && decl.get_css_wide_keyword().is_some()
- })
- }
-
- /// Returns a custom properties map which is the result of cascading custom
- /// properties in this declaration block along with context's custom
- /// properties.
- pub fn cascade_custom_properties_with_context(
- &self,
- context: &Context,
- ) -> Option<Arc<crate::custom_properties::CustomPropertiesMap>> {
- self.cascade_custom_properties(context.style().custom_properties(), context.device())
- }
-
- /// Returns a custom properties map which is the result of cascading custom
- /// properties in this declaration block along with the given custom
- /// properties.
- fn cascade_custom_properties(
- &self,
- inherited_custom_properties: Option<&Arc<crate::custom_properties::CustomPropertiesMap>>,
- device: &Device,
- ) -> Option<Arc<crate::custom_properties::CustomPropertiesMap>> {
- let mut builder = CustomPropertiesBuilder::new(inherited_custom_properties, device);
-
- for declaration in self.normal_declaration_iter() {
- if let PropertyDeclaration::Custom(ref declaration) = *declaration {
- builder.cascade(
- declaration,
- CascadePriority::new(
- CascadeLevel::same_tree_author_normal(),
- LayerOrder::root(),
- ),
- );
- }
- }
-
- builder.build()
- }
-
- /// Like the method on ToCss, but without the type parameter to avoid
- /// accidentally monomorphizing this large function multiple times for
- /// different writers.
- ///
- /// https://drafts.csswg.org/cssom/#serialize-a-css-declaration-block
- pub fn to_css(&self, dest: &mut CssStringWriter) -> fmt::Result {
- let mut is_first_serialization = true; // trailing serializations should have a prepended space
-
- // Step 1 -> dest = result list
-
- // Step 2
- //
- // NOTE(emilio): We reuse this set for both longhands and shorthands
- // with subtly different meaning. For longhands, only longhands that
- // have actually been serialized (either by themselves, or as part of a
- // shorthand) appear here. For shorthands, all the shorthands that we've
- // attempted to serialize appear here.
- let mut already_serialized = NonCustomPropertyIdSet::new();
-
- // Step 3
- 'declaration_loop: for (declaration, importance) in self.declaration_importance_iter() {
- // Step 3.1
- let property = declaration.id();
- let longhand_id = match property {
- PropertyDeclarationId::Longhand(id) => id,
- PropertyDeclarationId::Custom(..) => {
- // Given the invariants that there are no duplicate
- // properties in a declaration block, and that custom
- // properties can't be part of a shorthand, we can just care
- // about them here.
- append_serialization(
- dest,
- &property,
- AppendableValue::Declaration(declaration),
- importance,
- &mut is_first_serialization,
- )?;
- continue;
- },
- };
-
- // Step 3.2
- if already_serialized.contains(longhand_id.into()) {
- continue;
- }
-
- // Steps 3.3 & 3.4
- for shorthand in longhand_id.shorthands() {
- // We already attempted to serialize this shorthand before.
- if already_serialized.contains(shorthand.into()) {
- continue;
- }
- already_serialized.insert(shorthand.into());
-
- if shorthand.is_legacy_shorthand() {
- continue;
- }
-
- // Step 3.3.1:
- // Let longhands be an array consisting of all CSS
- // declarations in declaration block’s declarations that
- // that are not in already serialized and have a property
- // name that maps to one of the shorthand properties in
- // shorthands.
- let longhands = {
- // TODO(emilio): This could just index in an array if we
- // remove pref-controlled longhands.
- let mut ids = LonghandIdSet::new();
- for longhand in shorthand.longhands() {
- ids.insert(longhand);
- }
- ids
- };
-
- // Step 3.4.2
- // If all properties that map to shorthand are not present
- // in longhands, continue with the steps labeled shorthand
- // loop.
- if !self.property_ids.longhands.contains_all(&longhands) {
- continue;
- }
-
- // Step 3.4.3:
- // Let current longhands be an empty array.
- let mut current_longhands = SmallVec::<[&_; 10]>::new();
- let mut logical_groups = LogicalGroupSet::new();
- let mut saw_one = false;
- let mut logical_mismatch = false;
- let mut seen = LonghandIdSet::new();
- let mut important_count = 0;
-
- // Step 3.4.4:
- // Append all CSS declarations in longhands that have a
- // property name that maps to shorthand to current longhands.
- for (declaration, importance) in self.declaration_importance_iter() {
- let longhand = match declaration.id() {
- PropertyDeclarationId::Longhand(id) => id,
- PropertyDeclarationId::Custom(..) => continue,
- };
-
- if longhands.contains(longhand) {
- saw_one = true;
- if importance.important() {
- important_count += 1;
- }
- current_longhands.push(declaration);
- if shorthand != ShorthandId::All {
- // All is special because it contains both physical
- // and logical longhands.
- if let Some(g) = longhand.logical_group() {
- logical_groups.insert(g);
- }
- seen.insert(longhand);
- if seen == longhands {
- break;
- }
- }
- } else if saw_one {
- if let Some(g) = longhand.logical_group() {
- if logical_groups.contains(g) {
- logical_mismatch = true;
- break;
- }
- }
- }
- }
-
- // 3.4.5:
- // If there is one or more CSS declarations in current
- // longhands have their important flag set and one or more
- // with it unset, continue with the steps labeled shorthand
- // loop.
- let is_important = important_count > 0;
- if is_important && important_count != current_longhands.len() {
- continue;
- }
-
- // 3.4.6:
- // If there’s any declaration in declaration block in between
- // the first and the last longhand in current longhands which
- // belongs to the same logical property group, but has a
- // different mapping logic as any of the longhands in current
- // longhands, and is not in current longhands, continue with
- // the steps labeled shorthand loop.
- if logical_mismatch {
- continue;
- }
-
- let importance = if is_important {
- Importance::Important
- } else {
- Importance::Normal
- };
-
- // 3.4.7:
- // Let value be the result of invoking serialize a CSS value
- // of current longhands.
- let appendable_value =
- match shorthand.get_shorthand_appendable_value(&current_longhands) {
- None => continue,
- Some(appendable_value) => appendable_value,
- };
-
- // We avoid re-serializing if we're already an
- // AppendableValue::Css.
- let mut v = CssString::new();
- let value = match appendable_value {
- AppendableValue::Css(css) => {
- debug_assert!(!css.is_empty());
- appendable_value
- },
- other => {
- append_declaration_value(&mut v, other)?;
-
- // 3.4.8:
- // If value is the empty string, continue with the
- // steps labeled shorthand loop.
- if v.is_empty() {
- continue;
- }
-
- AppendableValue::Css({
- // Safety: serialization only generates valid utf-8.
- #[cfg(feature = "gecko")]
- unsafe {
- v.as_str_unchecked()
- }
- #[cfg(feature = "servo")]
- &v
- })
- },
- };
-
- // 3.4.9:
- // Let serialized declaration be the result of invoking
- // serialize a CSS declaration with property name shorthand,
- // value value, and the important flag set if the CSS
- // declarations in current longhands have their important
- // flag set.
- //
- // 3.4.10:
- // Append serialized declaration to list.
- append_serialization(
- dest,
- &shorthand,
- value,
- importance,
- &mut is_first_serialization,
- )?;
-
- // 3.4.11:
- // Append the property names of all items of current
- // longhands to already serialized.
- for current_longhand in &current_longhands {
- let longhand_id = match current_longhand.id() {
- PropertyDeclarationId::Longhand(id) => id,
- PropertyDeclarationId::Custom(..) => unreachable!(),
- };
-
- // Substep 9
- already_serialized.insert(longhand_id.into());
- }
-
- // 3.4.12:
- // Continue with the steps labeled declaration loop.
- continue 'declaration_loop;
- }
-
- // Steps 3.5, 3.6 & 3.7:
- // Let value be the result of invoking serialize a CSS value of
- // declaration.
- //
- // Let serialized declaration be the result of invoking
- // serialize a CSS declaration with property name property,
- // value value, and the important flag set if declaration has
- // its important flag set.
- //
- // Append serialized declaration to list.
- append_serialization(
- dest,
- &property,
- AppendableValue::Declaration(declaration),
- importance,
- &mut is_first_serialization,
- )?;
-
- // Step 3.8:
- // Append property to already serialized.
- already_serialized.insert(longhand_id.into());
- }
-
- // Step 4
- Ok(())
- }
-}
-
-/// A convenient enum to represent different kinds of stuff that can represent a
-/// _value_ in the serialization of a property declaration.
-pub enum AppendableValue<'a, 'b: 'a> {
- /// A given declaration, of which we'll serialize just the value.
- Declaration(&'a PropertyDeclaration),
- /// A set of declarations for a given shorthand.
- ///
- /// FIXME: This needs more docs, where are the shorthands expanded? We print
- /// the property name before-hand, don't we?
- DeclarationsForShorthand(ShorthandId, &'a [&'b PropertyDeclaration]),
- /// A raw CSS string, coming for example from a property with CSS variables,
- /// or when storing a serialized shorthand value before appending directly.
- Css(&'a str),
-}
-
-/// Potentially appends whitespace after the first (property: value;) pair.
-fn handle_first_serialization<W>(dest: &mut W, is_first_serialization: &mut bool) -> fmt::Result
-where
- W: Write,
-{
- if !*is_first_serialization {
- dest.write_char(' ')
- } else {
- *is_first_serialization = false;
- Ok(())
- }
-}
-
-/// Append a given kind of appendable value to a serialization.
-pub fn append_declaration_value<'a, 'b: 'a>(
- dest: &mut CssStringWriter,
- appendable_value: AppendableValue<'a, 'b>,
-) -> fmt::Result {
- match appendable_value {
- AppendableValue::Css(css) => dest.write_str(css),
- AppendableValue::Declaration(decl) => decl.to_css(dest),
- AppendableValue::DeclarationsForShorthand(shorthand, decls) => {
- shorthand.longhands_to_css(decls, dest)
- },
- }
-}
-
-/// Append a given property and value pair to a serialization.
-pub fn append_serialization<'a, 'b: 'a, N>(
- dest: &mut CssStringWriter,
- property_name: &N,
- appendable_value: AppendableValue<'a, 'b>,
- importance: Importance,
- is_first_serialization: &mut bool,
-) -> fmt::Result
-where
- N: ToCss,
-{
- handle_first_serialization(dest, is_first_serialization)?;
-
- property_name.to_css(&mut CssWriter::new(dest))?;
- dest.write_str(": ")?;
-
- append_declaration_value(dest, appendable_value)?;
-
- if importance.important() {
- dest.write_str(" !important")?;
- }
-
- dest.write_char(';')
-}
-
-/// A helper to parse the style attribute of an element, in order for this to be
-/// shared between Servo and Gecko.
-///
-/// Inline because we call this cross-crate.
-#[inline]
-pub fn parse_style_attribute(
- input: &str,
- url_data: &UrlExtraData,
- error_reporter: Option<&dyn ParseErrorReporter>,
- quirks_mode: QuirksMode,
- rule_type: CssRuleType,
-) -> PropertyDeclarationBlock {
- let context = ParserContext::new(
- Origin::Author,
- url_data,
- Some(rule_type),
- ParsingMode::DEFAULT,
- quirks_mode,
- /* namespaces = */ Default::default(),
- error_reporter,
- None,
- );
-
- let mut input = ParserInput::new(input);
- parse_property_declaration_list(&context, &mut Parser::new(&mut input), None)
-}
-
-/// Parse a given property declaration. Can result in multiple
-/// `PropertyDeclaration`s when expanding a shorthand, for example.
-///
-/// This does not attempt to parse !important at all.
-#[inline]
-pub fn parse_one_declaration_into(
- declarations: &mut SourcePropertyDeclaration,
- id: PropertyId,
- input: &str,
- origin: Origin,
- url_data: &UrlExtraData,
- error_reporter: Option<&dyn ParseErrorReporter>,
- parsing_mode: ParsingMode,
- quirks_mode: QuirksMode,
- rule_type: CssRuleType,
-) -> Result<(), ()> {
- let context = ParserContext::new(
- origin,
- url_data,
- Some(rule_type),
- parsing_mode,
- quirks_mode,
- /* namespaces = */ Default::default(),
- error_reporter,
- None,
- );
-
- let property_id_for_error_reporting = if context.error_reporting_enabled() {
- Some(id.clone())
- } else {
- None
- };
-
- let mut input = ParserInput::new(input);
- let mut parser = Parser::new(&mut input);
- let start_position = parser.position();
- parser
- .parse_entirely(|parser| {
- PropertyDeclaration::parse_into(declarations, id, &context, parser)
- })
- .map_err(|err| {
- if context.error_reporting_enabled() {
- report_one_css_error(
- &context,
- None,
- None,
- err,
- parser.slice_from(start_position),
- property_id_for_error_reporting,
- )
- }
- })
-}
-
-/// A struct to parse property declarations.
-struct PropertyDeclarationParser<'a, 'b: 'a, 'i> {
- context: &'a ParserContext<'b>,
- state: &'a mut DeclarationParserState<'i>,
-}
-
-/// The state needed to parse a declaration block.
-///
-/// It stores declarations in output_block.
-#[derive(Default)]
-pub struct DeclarationParserState<'i> {
- /// The output block where results are stored.
- output_block: PropertyDeclarationBlock,
- /// Declarations from the last declaration parsed. (note that a shorthand might expand to
- /// multiple declarations).
- declarations: SourcePropertyDeclaration,
- /// The importance from the last declaration parsed.
- importance: Importance,
- /// A list of errors that have happened so far. Not all of them might be reported.
- errors: SmallParseErrorVec<'i>,
- /// The last parsed property id, if any.
- last_parsed_property_id: Option<PropertyId>,
-}
-
-impl<'i> DeclarationParserState<'i> {
- /// Returns whether any parsed declarations have been parsed so far.
- pub fn has_parsed_declarations(&self) -> bool {
- !self.output_block.is_empty()
- }
-
- /// Takes the parsed declarations.
- pub fn take_declarations(&mut self) -> PropertyDeclarationBlock {
- std::mem::take(&mut self.output_block)
- }
-
- /// Parse a single declaration value.
- pub fn parse_value<'t>(
- &mut self,
- context: &ParserContext,
- name: CowRcStr<'i>,
- input: &mut Parser<'i, 't>,
- ) -> Result<(), ParseError<'i>> {
- let id = match PropertyId::parse(&name, context) {
- Ok(id) => id,
- Err(..) => {
- return Err(input.new_custom_error(StyleParseErrorKind::UnknownProperty(name)));
- },
- };
- if context.error_reporting_enabled() {
- self.last_parsed_property_id = Some(id.clone());
- }
- input.parse_until_before(Delimiter::Bang, |input| {
- PropertyDeclaration::parse_into(&mut self.declarations, id, context, input)
- })?;
- self.importance = match input.try_parse(parse_important) {
- Ok(()) => Importance::Important,
- Err(_) => Importance::Normal,
- };
- // In case there is still unparsed text in the declaration, we should roll back.
- input.expect_exhausted()?;
- self.output_block
- .extend(self.declarations.drain(), self.importance);
- // We've successfully parsed a declaration, so forget about
- // `last_parsed_property_id`. It'd be wrong to associate any
- // following error with this property.
- self.last_parsed_property_id = None;
- Ok(())
- }
-
- /// Reports any CSS errors that have ocurred if needed.
- #[inline]
- pub fn report_errors_if_needed(
- &mut self,
- context: &ParserContext,
- selectors: Option<&SelectorList<SelectorImpl>>,
- ) {
- if self.errors.is_empty() {
- return;
- }
- self.do_report_css_errors(context, selectors);
- }
-
- #[cold]
- fn do_report_css_errors(
- &mut self,
- context: &ParserContext,
- selectors: Option<&SelectorList<SelectorImpl>>,
- ) {
- for (error, slice, property) in self.errors.drain(..) {
- report_one_css_error(
- context,
- Some(&self.output_block),
- selectors,
- error,
- slice,
- property,
- )
- }
- }
-
- /// Resets the declaration parser state, and reports the error if needed.
- #[inline]
- pub fn did_error(&mut self, context: &ParserContext, error: ParseError<'i>, slice: &'i str) {
- self.declarations.clear();
- if !context.error_reporting_enabled() {
- return;
- }
- let property = self.last_parsed_property_id.take();
- self.errors.push((error, slice, property));
- }
-}
-
-/// Default methods reject all at rules.
-impl<'a, 'b, 'i> AtRuleParser<'i> for PropertyDeclarationParser<'a, 'b, 'i> {
- type Prelude = ();
- type AtRule = ();
- type Error = StyleParseErrorKind<'i>;
-}
-
-/// Default methods reject all rules.
-impl<'a, 'b, 'i> QualifiedRuleParser<'i> for PropertyDeclarationParser<'a, 'b, 'i> {
- type Prelude = ();
- type QualifiedRule = ();
- type Error = StyleParseErrorKind<'i>;
-}
-
-/// Based on NonMozillaVendorIdentifier from Gecko's CSS parser.
-fn is_non_mozilla_vendor_identifier(name: &str) -> bool {
- (name.starts_with("-") && !name.starts_with("-moz-")) || name.starts_with("_")
-}
-
-impl<'a, 'b, 'i> DeclarationParser<'i> for PropertyDeclarationParser<'a, 'b, 'i> {
- type Declaration = ();
- type Error = StyleParseErrorKind<'i>;
-
- fn parse_value<'t>(
- &mut self,
- name: CowRcStr<'i>,
- input: &mut Parser<'i, 't>,
- ) -> Result<(), ParseError<'i>> {
- self.state.parse_value(self.context, name, input)
- }
-}
-
-impl<'a, 'b, 'i> RuleBodyItemParser<'i, (), StyleParseErrorKind<'i>>
- for PropertyDeclarationParser<'a, 'b, 'i>
-{
- fn parse_declarations(&self) -> bool {
- true
- }
- // TODO(emilio): Nesting.
- fn parse_qualified(&self) -> bool {
- false
- }
-}
-
-type SmallParseErrorVec<'i> = SmallVec<[(ParseError<'i>, &'i str, Option<PropertyId>); 2]>;
-
-fn alias_of_known_property(name: &str) -> Option<PropertyId> {
- let mut prefixed = String::with_capacity(name.len() + 5);
- prefixed.push_str("-moz-");
- prefixed.push_str(name);
- PropertyId::parse_enabled_for_all_content(&prefixed).ok()
-}
-
-#[cold]
-fn report_one_css_error<'i>(
- context: &ParserContext,
- block: Option<&PropertyDeclarationBlock>,
- selectors: Option<&SelectorList<SelectorImpl>>,
- mut error: ParseError<'i>,
- slice: &str,
- property: Option<PropertyId>,
-) {
- debug_assert!(context.error_reporting_enabled());
-
- fn all_properties_in_block(block: &PropertyDeclarationBlock, property: &PropertyId) -> bool {
- match property.as_shorthand() {
- Ok(id) => id
- .longhands()
- .all(|longhand| block.contains(PropertyDeclarationId::Longhand(longhand))),
- Err(longhand_or_custom) => block.contains(longhand_or_custom),
- }
- }
-
- if let ParseErrorKind::Custom(StyleParseErrorKind::UnknownProperty(ref name)) = error.kind {
- if is_non_mozilla_vendor_identifier(name) {
- // If the unrecognized property looks like a vendor-specific property,
- // silently ignore it instead of polluting the error output.
- return;
- }
- if let Some(alias) = alias_of_known_property(name) {
- // This is an unknown property, but its -moz-* version is known.
- // We don't want to report error if the -moz-* version is already
- // specified.
- if let Some(block) = block {
- if all_properties_in_block(block, &alias) {
- return;
- }
- }
- }
- }
-
- if let Some(ref property) = property {
- if let Some(block) = block {
- if all_properties_in_block(block, property) {
- return;
- }
- }
- error = match *property {
- PropertyId::Custom(ref c) => {
- StyleParseErrorKind::new_invalid(format!("--{}", c), error)
- },
- _ => StyleParseErrorKind::new_invalid(property.non_custom_id().unwrap().name(), error),
- };
- }
-
- let location = error.location;
- let error = ContextualParseError::UnsupportedPropertyDeclaration(slice, error, selectors);
- context.log_css_error(location, error);
-}
-
-/// Parse a list of property declarations and return a property declaration
-/// block.
-pub fn parse_property_declaration_list(
- context: &ParserContext,
- input: &mut Parser,
- selectors: Option<&SelectorList<SelectorImpl>>,
-) -> PropertyDeclarationBlock {
- let mut state = DeclarationParserState::default();
- let mut parser = PropertyDeclarationParser {
- context,
- state: &mut state,
- };
- let mut iter = RuleBodyParser::new(input, &mut parser);
- while let Some(declaration) = iter.next() {
- match declaration {
- Ok(()) => {},
- Err((error, slice)) => iter.parser.state.did_error(context, error, slice),
- }
- }
- parser.state.report_errors_if_needed(context, selectors);
- state.output_block
-}
diff --git a/components/style/properties/gecko.mako.rs b/components/style/properties/gecko.mako.rs
deleted file mode 100644
index 4fe290980d8..00000000000
--- a/components/style/properties/gecko.mako.rs
+++ /dev/null
@@ -1,1971 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
-
-// `data` comes from components/style/properties.mako.rs; see build.rs for more details.
-
-<%!
- from data import to_camel_case, to_camel_case_lower
- from data import Keyword
-%>
-<%namespace name="helpers" file="/helpers.mako.rs" />
-
-use crate::Atom;
-use app_units::Au;
-use crate::computed_value_flags::*;
-use crate::custom_properties::CustomPropertiesMap;
-use crate::gecko_bindings::bindings;
-% for style_struct in data.style_structs:
-use crate::gecko_bindings::bindings::Gecko_Construct_Default_${style_struct.gecko_ffi_name};
-use crate::gecko_bindings::bindings::Gecko_CopyConstruct_${style_struct.gecko_ffi_name};
-use crate::gecko_bindings::bindings::Gecko_Destroy_${style_struct.gecko_ffi_name};
-% endfor
-use crate::gecko_bindings::bindings::Gecko_CopyCounterStyle;
-use crate::gecko_bindings::bindings::Gecko_EnsureImageLayersLength;
-use crate::gecko_bindings::bindings::Gecko_nsStyleFont_SetLang;
-use crate::gecko_bindings::bindings::Gecko_nsStyleFont_CopyLangFrom;
-use crate::gecko_bindings::structs;
-use crate::gecko_bindings::structs::nsCSSPropertyID;
-use crate::gecko_bindings::structs::mozilla::PseudoStyleType;
-use crate::gecko::data::PerDocumentStyleData;
-use crate::logical_geometry::WritingMode;
-use crate::media_queries::Device;
-use crate::properties::longhands;
-use crate::rule_tree::StrongRuleNode;
-use crate::selector_parser::PseudoElement;
-use servo_arc::{Arc, UniqueArc};
-use std::mem::{forget, MaybeUninit, ManuallyDrop};
-use std::{cmp, ops, ptr};
-use crate::values::{self, CustomIdent, KeyframesName};
-use crate::values::computed::{BorderStyle, Percentage, Time, TransitionProperty};
-use crate::values::computed::font::FontSize;
-use crate::values::generics::column::ColumnCount;
-
-
-pub mod style_structs {
- % for style_struct in data.style_structs:
- pub use super::${style_struct.gecko_struct_name} as ${style_struct.name};
-
- unsafe impl Send for ${style_struct.name} {}
- unsafe impl Sync for ${style_struct.name} {}
- % endfor
-}
-
-/// FIXME(emilio): This is completely duplicated with the other properties code.
-pub type ComputedValuesInner = structs::ServoComputedData;
-
-#[repr(C)]
-pub struct ComputedValues(structs::mozilla::ComputedStyle);
-
-impl ComputedValues {
- #[inline]
- pub (crate) fn as_gecko_computed_style(&self) -> &structs::ComputedStyle {
- &self.0
- }
-
- pub fn new(
- pseudo: Option<<&PseudoElement>,
- custom_properties: Option<Arc<CustomPropertiesMap>>,
- writing_mode: WritingMode,
- flags: ComputedValueFlags,
- rules: Option<StrongRuleNode>,
- visited_style: Option<Arc<ComputedValues>>,
- % for style_struct in data.style_structs:
- ${style_struct.ident}: Arc<style_structs::${style_struct.name}>,
- % endfor
- ) -> Arc<Self> {
- ComputedValuesInner::new(
- custom_properties,
- writing_mode,
- flags,
- rules,
- visited_style,
- % for style_struct in data.style_structs:
- ${style_struct.ident},
- % endfor
- ).to_outer(pseudo)
- }
-
- pub fn default_values(doc: &structs::Document) -> Arc<Self> {
- ComputedValuesInner::new(
- /* custom_properties = */ None,
- /* writing_mode = */ WritingMode::empty(), // FIXME(bz): This seems dubious
- ComputedValueFlags::empty(),
- /* rules = */ None,
- /* visited_style = */ None,
- % for style_struct in data.style_structs:
- style_structs::${style_struct.name}::default(doc),
- % endfor
- ).to_outer(None)
- }
-
- /// Converts the computed values to an Arc<> from a reference.
- pub fn to_arc(&self) -> Arc<Self> {
- // SAFETY: We're guaranteed to be allocated as an Arc<> since the
- // functions above are the only ones that create ComputedValues
- // instances in Gecko (and that must be the case since ComputedValues'
- // member is private).
- unsafe { Arc::from_raw_addrefed(self) }
- }
-
- #[inline]
- pub fn is_pseudo_style(&self) -> bool {
- self.0.mPseudoType != PseudoStyleType::NotPseudo
- }
-
- #[inline]
- pub fn pseudo(&self) -> Option<PseudoElement> {
- if !self.is_pseudo_style() {
- return None;
- }
- PseudoElement::from_pseudo_type(self.0.mPseudoType)
- }
-
- #[inline]
- pub fn is_first_line_style(&self) -> bool {
- self.pseudo() == Some(PseudoElement::FirstLine)
- }
-
- /// Returns true if the display property is changed from 'none' to others.
- pub fn is_display_property_changed_from_none(
- &self,
- old_values: Option<<&ComputedValues>
- ) -> bool {
- use crate::properties::longhands::display::computed_value::T as Display;
-
- old_values.map_or(false, |old| {
- let old_display_style = old.get_box().clone_display();
- let new_display_style = self.get_box().clone_display();
- old_display_style == Display::None &&
- new_display_style != Display::None
- })
- }
-
-}
-
-impl Drop for ComputedValues {
- fn drop(&mut self) {
- // XXX this still relies on the destructor of ComputedValuesInner to run on the rust side,
- // that's pretty wild.
- unsafe {
- bindings::Gecko_ComputedStyle_Destroy(&mut self.0);
- }
- }
-}
-
-unsafe impl Sync for ComputedValues {}
-unsafe impl Send for ComputedValues {}
-
-impl Clone for ComputedValues {
- fn clone(&self) -> Self {
- unreachable!()
- }
-}
-
-impl Clone for ComputedValuesInner {
- fn clone(&self) -> Self {
- ComputedValuesInner {
- % for style_struct in data.style_structs:
- ${style_struct.gecko_name}: Arc::into_raw(unsafe { Arc::from_raw_addrefed(self.${style_struct.name_lower}_ptr()) }) as *const _,
- % endfor
- custom_properties: self.custom_properties.clone(),
- writing_mode: self.writing_mode.clone(),
- flags: self.flags.clone(),
- rules: self.rules.clone(),
- visited_style: if self.visited_style.is_null() {
- ptr::null()
- } else {
- Arc::into_raw(unsafe { Arc::from_raw_addrefed(self.visited_style_ptr()) }) as *const _
- },
- }
- }
-}
-
-
-impl Drop for ComputedValuesInner {
- fn drop(&mut self) {
- % for style_struct in data.style_structs:
- let _ = unsafe { Arc::from_raw(self.${style_struct.name_lower}_ptr()) };
- % endfor
- if !self.visited_style.is_null() {
- let _ = unsafe { Arc::from_raw(self.visited_style_ptr()) };
- }
- }
-}
-
-impl ComputedValuesInner {
- pub fn new(
- custom_properties: Option<Arc<CustomPropertiesMap>>,
- writing_mode: WritingMode,
- flags: ComputedValueFlags,
- rules: Option<StrongRuleNode>,
- visited_style: Option<Arc<ComputedValues>>,
- % for style_struct in data.style_structs:
- ${style_struct.ident}: Arc<style_structs::${style_struct.name}>,
- % endfor
- ) -> Self {
- Self {
- custom_properties,
- writing_mode,
- rules,
- visited_style: visited_style.map_or(ptr::null(), |p| Arc::into_raw(p)) as *const _,
- flags,
- % for style_struct in data.style_structs:
- ${style_struct.gecko_name}: Arc::into_raw(${style_struct.ident}) as *const _,
- % endfor
- }
- }
-
- fn to_outer(self, pseudo: Option<<&PseudoElement>) -> Arc<ComputedValues> {
- let pseudo_ty = match pseudo {
- Some(p) => p.pseudo_type(),
- None => structs::PseudoStyleType::NotPseudo,
- };
- unsafe {
- let mut arc = UniqueArc::<ComputedValues>::new_uninit();
- bindings::Gecko_ComputedStyle_Init(
- arc.as_mut_ptr() as *mut _,
- &self,
- pseudo_ty,
- );
- // We're simulating move semantics by having C++ do a memcpy and
- // then forgetting it on this end.
- forget(self);
- UniqueArc::assume_init(arc).shareable()
- }
- }
-}
-
-impl ops::Deref for ComputedValues {
- type Target = ComputedValuesInner;
- #[inline]
- fn deref(&self) -> &ComputedValuesInner {
- &self.0.mSource
- }
-}
-
-impl ops::DerefMut for ComputedValues {
- #[inline]
- fn deref_mut(&mut self) -> &mut ComputedValuesInner {
- &mut self.0.mSource
- }
-}
-
-impl ComputedValuesInner {
- /// Returns true if the value of the `content` property would make a
- /// pseudo-element not rendered.
- #[inline]
- pub fn ineffective_content_property(&self) -> bool {
- self.get_counters().ineffective_content_property()
- }
-
- #[inline]
- fn visited_style_ptr(&self) -> *const ComputedValues {
- self.visited_style as *const _
- }
-
- /// Returns the visited style, if any.
- pub fn visited_style(&self) -> Option<<&ComputedValues> {
- unsafe { self.visited_style_ptr().as_ref() }
- }
-
- % for style_struct in data.style_structs:
- #[inline]
- fn ${style_struct.name_lower}_ptr(&self) -> *const style_structs::${style_struct.name} {
- // This is sound because the wrapper we create is repr(transparent).
- self.${style_struct.gecko_name} as *const _
- }
-
- #[inline]
- pub fn clone_${style_struct.name_lower}(&self) -> Arc<style_structs::${style_struct.name}> {
- unsafe { Arc::from_raw_addrefed(self.${style_struct.name_lower}_ptr()) }
- }
- #[inline]
- pub fn get_${style_struct.name_lower}(&self) -> &style_structs::${style_struct.name} {
- unsafe { &*self.${style_struct.name_lower}_ptr() }
- }
-
- #[inline]
- pub fn mutate_${style_struct.name_lower}(&mut self) -> &mut style_structs::${style_struct.name} {
- unsafe {
- let mut arc = Arc::from_raw(self.${style_struct.name_lower}_ptr());
- let ptr = Arc::make_mut(&mut arc) as *mut _;
- // Sound for the same reason _ptr() is sound.
- self.${style_struct.gecko_name} = Arc::into_raw(arc) as *const _;
- &mut *ptr
- }
- }
- % endfor
-}
-
-<%def name="impl_simple_setter(ident, gecko_ffi_name)">
- #[allow(non_snake_case)]
- pub fn set_${ident}(&mut self, v: longhands::${ident}::computed_value::T) {
- ${set_gecko_property(gecko_ffi_name, "From::from(v)")}
- }
-</%def>
-
-<%def name="impl_simple_clone(ident, gecko_ffi_name)">
- #[allow(non_snake_case)]
- pub fn clone_${ident}(&self) -> longhands::${ident}::computed_value::T {
- From::from(self.${gecko_ffi_name}.clone())
- }
-</%def>
-
-<%def name="impl_simple_copy(ident, gecko_ffi_name, *kwargs)">
- #[allow(non_snake_case)]
- pub fn copy_${ident}_from(&mut self, other: &Self) {
- self.${gecko_ffi_name} = other.${gecko_ffi_name}.clone();
- }
-
- #[allow(non_snake_case)]
- pub fn reset_${ident}(&mut self, other: &Self) {
- self.copy_${ident}_from(other)
- }
-</%def>
-
-<%!
-def get_gecko_property(ffi_name, self_param = "self"):
- return "%s.%s" % (self_param, ffi_name)
-
-def set_gecko_property(ffi_name, expr):
- return "self.%s = %s;" % (ffi_name, expr)
-%>
-
-<%def name="impl_keyword_setter(ident, gecko_ffi_name, keyword, cast_type='u8')">
- #[allow(non_snake_case)]
- pub fn set_${ident}(&mut self, v: longhands::${ident}::computed_value::T) {
- use crate::properties::longhands::${ident}::computed_value::T as Keyword;
- // FIXME(bholley): Align binary representations and ditch |match| for cast + static_asserts
- let result = match v {
- % for value in keyword.values_for('gecko'):
- Keyword::${to_camel_case(value)} =>
- structs::${keyword.gecko_constant(value)} ${keyword.maybe_cast(cast_type)},
- % endfor
- };
- ${set_gecko_property(gecko_ffi_name, "result")}
- }
-</%def>
-
-<%def name="impl_keyword_clone(ident, gecko_ffi_name, keyword, cast_type='u8')">
- #[allow(non_snake_case)]
- pub fn clone_${ident}(&self) -> longhands::${ident}::computed_value::T {
- use crate::properties::longhands::${ident}::computed_value::T as Keyword;
- // FIXME(bholley): Align binary representations and ditch |match| for cast + static_asserts
-
- // Some constant macros in the gecko are defined as negative integer(e.g. font-stretch).
- // And they are convert to signed integer in Rust bindings. We need to cast then
- // as signed type when we have both signed/unsigned integer in order to use them
- // as match's arms.
- // Also, to use same implementation here we use casted constant if we have only singed values.
- % if keyword.gecko_enum_prefix is None:
- % for value in keyword.values_for('gecko'):
- const ${keyword.casted_constant_name(value, cast_type)} : ${cast_type} =
- structs::${keyword.gecko_constant(value)} as ${cast_type};
- % endfor
-
- match ${get_gecko_property(gecko_ffi_name)} as ${cast_type} {
- % for value in keyword.values_for('gecko'):
- ${keyword.casted_constant_name(value, cast_type)} => Keyword::${to_camel_case(value)},
- % endfor
- % if keyword.gecko_inexhaustive:
- _ => panic!("Found unexpected value in style struct for ${ident} property"),
- % endif
- }
- % else:
- match ${get_gecko_property(gecko_ffi_name)} {
- % for value in keyword.values_for('gecko'):
- structs::${keyword.gecko_constant(value)} => Keyword::${to_camel_case(value)},
- % endfor
- % if keyword.gecko_inexhaustive:
- _ => panic!("Found unexpected value in style struct for ${ident} property"),
- % endif
- }
- % endif
- }
-</%def>
-
-<%def name="impl_keyword(ident, gecko_ffi_name, keyword, cast_type='u8', **kwargs)">
-<%call expr="impl_keyword_setter(ident, gecko_ffi_name, keyword, cast_type, **kwargs)"></%call>
-<%call expr="impl_simple_copy(ident, gecko_ffi_name, **kwargs)"></%call>
-<%call expr="impl_keyword_clone(ident, gecko_ffi_name, keyword, cast_type)"></%call>
-</%def>
-
-<%def name="impl_simple(ident, gecko_ffi_name)">
-<%call expr="impl_simple_setter(ident, gecko_ffi_name)"></%call>
-<%call expr="impl_simple_copy(ident, gecko_ffi_name)"></%call>
-<%call expr="impl_simple_clone(ident, gecko_ffi_name)"></%call>
-</%def>
-
-<%def name="impl_border_width(ident, gecko_ffi_name, inherit_from)">
- #[allow(non_snake_case)]
- pub fn set_${ident}(&mut self, v: Au) {
- let value = v.0;
- self.${inherit_from} = value;
- self.${gecko_ffi_name} = value;
- }
-
- #[allow(non_snake_case)]
- pub fn copy_${ident}_from(&mut self, other: &Self) {
- self.${inherit_from} = other.${inherit_from};
- // NOTE: This is needed to easily handle the `unset` and `initial`
- // keywords, which are implemented calling this function.
- //
- // In practice, this means that we may have an incorrect value here, but
- // we'll adjust that properly in the style fixup phase.
- //
- // FIXME(emilio): We could clean this up a bit special-casing the reset_
- // function below.
- self.${gecko_ffi_name} = other.${inherit_from};
- }
-
- #[allow(non_snake_case)]
- pub fn reset_${ident}(&mut self, other: &Self) {
- self.copy_${ident}_from(other)
- }
-
- #[allow(non_snake_case)]
- pub fn clone_${ident}(&self) -> Au {
- Au(self.${gecko_ffi_name})
- }
-</%def>
-
-<%def name="impl_split_style_coord(ident, gecko_ffi_name, index)">
- #[allow(non_snake_case)]
- pub fn set_${ident}(&mut self, v: longhands::${ident}::computed_value::T) {
- self.${gecko_ffi_name}.${index} = v;
- }
- #[allow(non_snake_case)]
- pub fn copy_${ident}_from(&mut self, other: &Self) {
- self.${gecko_ffi_name}.${index} =
- other.${gecko_ffi_name}.${index}.clone();
- }
- #[allow(non_snake_case)]
- pub fn reset_${ident}(&mut self, other: &Self) {
- self.copy_${ident}_from(other)
- }
-
- #[allow(non_snake_case)]
- pub fn clone_${ident}(&self) -> longhands::${ident}::computed_value::T {
- self.${gecko_ffi_name}.${index}.clone()
- }
-</%def>
-
-<%def name="copy_sides_style_coord(ident)">
- <% gecko_ffi_name = "m" + to_camel_case(ident) %>
- #[allow(non_snake_case)]
- pub fn copy_${ident}_from(&mut self, other: &Self) {
- % for side in SIDES:
- self.${gecko_ffi_name}.data_at_mut(${side.index})
- .copy_from(&other.${gecko_ffi_name}.data_at(${side.index}));
- % endfor
- ${ caller.body() }
- }
-
- #[allow(non_snake_case)]
- pub fn reset_${ident}(&mut self, other: &Self) {
- self.copy_${ident}_from(other)
- }
-</%def>
-
-<%def name="impl_corner_style_coord(ident, gecko_ffi_name, corner)">
- #[allow(non_snake_case)]
- pub fn set_${ident}(&mut self, v: longhands::${ident}::computed_value::T) {
- self.${gecko_ffi_name}.${corner} = v;
- }
- #[allow(non_snake_case)]
- pub fn copy_${ident}_from(&mut self, other: &Self) {
- self.${gecko_ffi_name}.${corner} =
- other.${gecko_ffi_name}.${corner}.clone();
- }
- #[allow(non_snake_case)]
- pub fn reset_${ident}(&mut self, other: &Self) {
- self.copy_${ident}_from(other)
- }
- #[allow(non_snake_case)]
- pub fn clone_${ident}(&self) -> longhands::${ident}::computed_value::T {
- self.${gecko_ffi_name}.${corner}.clone()
- }
-</%def>
-
-<%def name="impl_logical(name, **kwargs)">
- ${helpers.logical_setter(name)}
-</%def>
-
-<%def name="impl_style_struct(style_struct)">
-/// A wrapper for ${style_struct.gecko_ffi_name}, to be able to manually construct / destruct /
-/// clone it.
-#[repr(transparent)]
-pub struct ${style_struct.gecko_struct_name}(ManuallyDrop<structs::${style_struct.gecko_ffi_name}>);
-
-impl ops::Deref for ${style_struct.gecko_struct_name} {
- type Target = structs::${style_struct.gecko_ffi_name};
- #[inline]
- fn deref(&self) -> &Self::Target {
- &self.0
- }
-}
-
-impl ops::DerefMut for ${style_struct.gecko_struct_name} {
- #[inline]
- fn deref_mut(&mut self) -> &mut <Self as ops::Deref>::Target {
- &mut self.0
- }
-}
-
-impl ${style_struct.gecko_struct_name} {
- #[allow(dead_code, unused_variables)]
- pub fn default(document: &structs::Document) -> Arc<Self> {
- unsafe {
- let mut result = UniqueArc::<Self>::new_uninit();
- // FIXME(bug 1595895): Zero the memory to keep valgrind happy, but
- // these looks like Valgrind false-positives at a quick glance.
- ptr::write_bytes::<Self>(result.as_mut_ptr(), 0, 1);
- Gecko_Construct_Default_${style_struct.gecko_ffi_name}(
- result.as_mut_ptr() as *mut _,
- document,
- );
- UniqueArc::assume_init(result).shareable()
- }
- }
-}
-
-impl Drop for ${style_struct.gecko_struct_name} {
- fn drop(&mut self) {
- unsafe {
- Gecko_Destroy_${style_struct.gecko_ffi_name}(&mut **self);
- }
- }
-}
-impl Clone for ${style_struct.gecko_struct_name} {
- fn clone(&self) -> Self {
- unsafe {
- let mut result = MaybeUninit::<Self>::uninit();
- // FIXME(bug 1595895): Zero the memory to keep valgrind happy, but
- // these looks like Valgrind false-positives at a quick glance.
- ptr::write_bytes::<Self>(result.as_mut_ptr(), 0, 1);
- Gecko_CopyConstruct_${style_struct.gecko_ffi_name}(result.as_mut_ptr() as *mut _, &**self);
- result.assume_init()
- }
- }
-}
-</%def>
-
-<%def name="impl_simple_type_with_conversion(ident, gecko_ffi_name)">
- #[allow(non_snake_case)]
- pub fn set_${ident}(&mut self, v: longhands::${ident}::computed_value::T) {
- self.${gecko_ffi_name} = From::from(v)
- }
-
- <% impl_simple_copy(ident, gecko_ffi_name) %>
-
- #[allow(non_snake_case)]
- pub fn clone_${ident}(&self) -> longhands::${ident}::computed_value::T {
- From::from(self.${gecko_ffi_name})
- }
-</%def>
-
-<%def name="impl_font_settings(ident, gecko_type, tag_type, value_type, gecko_value_type)">
- <%
- gecko_ffi_name = to_camel_case_lower(ident)
- %>
-
- pub fn set_${ident}(&mut self, v: longhands::${ident}::computed_value::T) {
- let iter = v.0.iter().map(|other| structs::${gecko_type} {
- mTag: other.tag.0,
- mValue: other.value as ${gecko_value_type},
- });
- self.mFont.${gecko_ffi_name}.assign_from_iter_pod(iter);
- }
-
- pub fn copy_${ident}_from(&mut self, other: &Self) {
- let iter = other.mFont.${gecko_ffi_name}.iter().map(|s| *s);
- self.mFont.${gecko_ffi_name}.assign_from_iter_pod(iter);
- }
-
- pub fn reset_${ident}(&mut self, other: &Self) {
- self.copy_${ident}_from(other)
- }
-
- pub fn clone_${ident}(&self) -> longhands::${ident}::computed_value::T {
- use crate::values::generics::font::{FontSettings, FontTag, ${tag_type}};
-
- FontSettings(
- self.mFont.${gecko_ffi_name}.iter().map(|gecko_font_setting| {
- ${tag_type} {
- tag: FontTag(gecko_font_setting.mTag),
- value: gecko_font_setting.mValue as ${value_type},
- }
- }).collect::<Vec<_>>().into_boxed_slice()
- )
- }
-</%def>
-
-<%def name="impl_trait(style_struct_name, skip_longhands='')">
-<%
- style_struct = next(x for x in data.style_structs if x.name == style_struct_name)
- longhands = [x for x in style_struct.longhands
- if not (skip_longhands == "*" or x.name in skip_longhands.split())]
-
- def longhand_method(longhand):
- args = dict(ident=longhand.ident, gecko_ffi_name=longhand.gecko_ffi_name)
-
- # get the method and pass additional keyword or type-specific arguments
- if longhand.logical:
- method = impl_logical
- args.update(name=longhand.name)
- elif longhand.keyword:
- method = impl_keyword
- args.update(keyword=longhand.keyword)
- if "font" in longhand.ident:
- args.update(cast_type=longhand.cast_type)
- else:
- method = impl_simple
-
- method(**args)
-%>
-impl ${style_struct.gecko_struct_name} {
- /*
- * Manually-Implemented Methods.
- */
- ${caller.body().strip()}
-
- /*
- * Auto-Generated Methods.
- */
- <%
- for longhand in longhands:
- longhand_method(longhand)
- %>
-}
-</%def>
-
-<%!
-class Side(object):
- def __init__(self, name, index):
- self.name = name
- self.ident = name.lower()
- self.index = index
-
-SIDES = [Side("Top", 0), Side("Right", 1), Side("Bottom", 2), Side("Left", 3)]
-CORNERS = ["top_left", "top_right", "bottom_right", "bottom_left"]
-%>
-
-#[allow(dead_code)]
-fn static_assert() {
- // Note: using the above technique with an enum hits a rust bug when |structs| is in a different crate.
- % for side in SIDES:
- { const DETAIL: u32 = [0][(structs::Side::eSide${side.name} as usize != ${side.index}) as usize]; let _ = DETAIL; }
- % endfor
-}
-
-
-<% skip_border_longhands = " ".join(["border-{0}-{1}".format(x.ident, y)
- for x in SIDES
- for y in ["style", "width"]] +
- ["border-{0}-radius".format(x.replace("_", "-"))
- for x in CORNERS]) %>
-
-<%self:impl_trait style_struct_name="Border"
- skip_longhands="${skip_border_longhands} border-image-repeat">
- % for side in SIDES:
- pub fn set_border_${side.ident}_style(&mut self, v: BorderStyle) {
- self.mBorderStyle[${side.index}] = v;
-
- // This is needed because the initial mComputedBorder value is set to
- // zero.
- //
- // In order to compute stuff, we start from the initial struct, and keep
- // going down the tree applying properties.
- //
- // That means, effectively, that when we set border-style to something
- // non-hidden, we should use the initial border instead.
- //
- // Servo stores the initial border-width in the initial struct, and then
- // adjusts as needed in the fixup phase. This means that the initial
- // struct is technically not valid without fixups, and that you lose
- // pretty much any sharing of the initial struct, which is kind of
- // unfortunate.
- //
- // Gecko has two fields for this, one that stores the "specified"
- // border, and other that stores the actual computed one. That means
- // that when we set border-style, border-width may change and we need to
- // sync back to the specified one. This is what this function does.
- //
- // Note that this doesn't impose any dependency in the order of
- // computation of the properties. This is only relevant if border-style
- // is specified, but border-width isn't. If border-width is specified at
- // some point, the two mBorder and mComputedBorder fields would be the
- // same already.
- //
- // Once we're here, we know that we'll run style fixups, so it's fine to
- // just copy the specified border here, we'll adjust it if it's
- // incorrect later.
- self.mComputedBorder.${side.ident} = self.mBorder.${side.ident};
- }
-
- pub fn copy_border_${side.ident}_style_from(&mut self, other: &Self) {
- self.set_border_${side.ident}_style(other.mBorderStyle[${side.index}]);
- }
-
- pub fn reset_border_${side.ident}_style(&mut self, other: &Self) {
- self.copy_border_${side.ident}_style_from(other);
- }
-
- #[inline]
- pub fn clone_border_${side.ident}_style(&self) -> BorderStyle {
- self.mBorderStyle[${side.index}]
- }
-
- ${impl_border_width("border_%s_width" % side.ident, "mComputedBorder.%s" % side.ident, "mBorder.%s" % side.ident)}
-
- pub fn border_${side.ident}_has_nonzero_width(&self) -> bool {
- self.mComputedBorder.${side.ident} != 0
- }
- % endfor
-
- % for corner in CORNERS:
- <% impl_corner_style_coord("border_%s_radius" % corner,
- "mBorderRadius",
- corner) %>
- % endfor
-
- <%
- border_image_repeat_keywords = ["Stretch", "Repeat", "Round", "Space"]
- %>
-
- pub fn set_border_image_repeat(&mut self, v: longhands::border_image_repeat::computed_value::T) {
- use crate::values::specified::border::BorderImageRepeatKeyword;
- use crate::gecko_bindings::structs::StyleBorderImageRepeat;
-
- % for i, side in enumerate(["H", "V"]):
- self.mBorderImageRepeat${side} = match v.${i} {
- % for keyword in border_image_repeat_keywords:
- BorderImageRepeatKeyword::${keyword} => StyleBorderImageRepeat::${keyword},
- % endfor
- };
- % endfor
- }
-
- pub fn copy_border_image_repeat_from(&mut self, other: &Self) {
- self.mBorderImageRepeatH = other.mBorderImageRepeatH;
- self.mBorderImageRepeatV = other.mBorderImageRepeatV;
- }
-
- pub fn reset_border_image_repeat(&mut self, other: &Self) {
- self.copy_border_image_repeat_from(other)
- }
-
- pub fn clone_border_image_repeat(&self) -> longhands::border_image_repeat::computed_value::T {
- use crate::values::specified::border::BorderImageRepeatKeyword;
- use crate::gecko_bindings::structs::StyleBorderImageRepeat;
-
- % for side in ["H", "V"]:
- let servo_${side.lower()} = match self.mBorderImageRepeat${side} {
- % for keyword in border_image_repeat_keywords:
- StyleBorderImageRepeat::${keyword} => BorderImageRepeatKeyword::${keyword},
- % endfor
- };
- % endfor
- longhands::border_image_repeat::computed_value::T(servo_h, servo_v)
- }
-</%self:impl_trait>
-
-<% skip_scroll_margin_longhands = " ".join(["scroll-margin-%s" % x.ident for x in SIDES]) %>
-<% skip_margin_longhands = " ".join(["margin-%s" % x.ident for x in SIDES]) %>
-<%self:impl_trait style_struct_name="Margin"
- skip_longhands="${skip_margin_longhands}
- ${skip_scroll_margin_longhands}">
- % for side in SIDES:
- <% impl_split_style_coord("margin_%s" % side.ident,
- "mMargin",
- side.index) %>
- <% impl_split_style_coord("scroll_margin_%s" % side.ident,
- "mScrollMargin",
- side.index) %>
- % endfor
-</%self:impl_trait>
-
-<% skip_scroll_padding_longhands = " ".join(["scroll-padding-%s" % x.ident for x in SIDES]) %>
-<% skip_padding_longhands = " ".join(["padding-%s" % x.ident for x in SIDES]) %>
-<%self:impl_trait style_struct_name="Padding"
- skip_longhands="${skip_padding_longhands}
- ${skip_scroll_padding_longhands}">
-
- % for side in SIDES:
- <% impl_split_style_coord("padding_%s" % side.ident,
- "mPadding",
- side.index) %>
- <% impl_split_style_coord("scroll_padding_%s" % side.ident, "mScrollPadding", side.index) %>
- % endfor
-</%self:impl_trait>
-
-<%self:impl_trait style_struct_name="Page">
-</%self:impl_trait>
-
-<% skip_position_longhands = " ".join(x.ident for x in SIDES) %>
-<%self:impl_trait style_struct_name="Position"
- skip_longhands="${skip_position_longhands}
- masonry-auto-flow">
- % for side in SIDES:
- <% impl_split_style_coord(side.ident, "mOffset", side.index) %>
- % endfor
- pub fn set_computed_justify_items(&mut self, v: values::specified::JustifyItems) {
- debug_assert_ne!(v.0, crate::values::specified::align::AlignFlags::LEGACY);
- self.mJustifyItems.computed = v;
- }
-
- ${impl_simple_type_with_conversion("masonry_auto_flow", "mMasonryAutoFlow")}
-</%self:impl_trait>
-
-<%self:impl_trait style_struct_name="Outline"
- skip_longhands="outline-style outline-width">
-
- pub fn set_outline_style(&mut self, v: longhands::outline_style::computed_value::T) {
- self.mOutlineStyle = v;
- // NB: This is needed to correctly handling the initial value of
- // outline-width when outline-style changes, see the
- // update_border_${side.ident} comment for more details.
- self.mActualOutlineWidth = self.mOutlineWidth;
- }
-
- pub fn copy_outline_style_from(&mut self, other: &Self) {
- self.set_outline_style(other.mOutlineStyle);
- }
-
- pub fn reset_outline_style(&mut self, other: &Self) {
- self.copy_outline_style_from(other)
- }
-
- pub fn clone_outline_style(&self) -> longhands::outline_style::computed_value::T {
- self.mOutlineStyle.clone()
- }
-
- ${impl_border_width("outline_width", "mActualOutlineWidth", "mOutlineWidth")}
-
- pub fn outline_has_nonzero_width(&self) -> bool {
- self.mActualOutlineWidth != 0
- }
-</%self:impl_trait>
-
-<% skip_font_longhands = """font-family font-size font-size-adjust font-weight
- font-style font-stretch -x-lang
- font-variant-alternates font-variant-east-asian
- font-variant-ligatures font-variant-numeric
- font-language-override font-feature-settings
- font-variation-settings -moz-min-font-size-ratio""" %>
-<%self:impl_trait style_struct_name="Font"
- skip_longhands="${skip_font_longhands}">
-
- // Negative numbers are invalid at parse time, but <integer> is still an
- // i32.
- <% impl_font_settings("font_feature_settings", "gfxFontFeature", "FeatureTagValue", "i32", "u32") %>
- <% impl_font_settings("font_variation_settings", "gfxFontVariation", "VariationValue", "f32", "f32") %>
-
- pub fn unzoom_fonts(&mut self, device: &Device) {
- use crate::values::generics::NonNegative;
- self.mSize = NonNegative(device.unzoom_text(self.mSize.0));
- self.mScriptUnconstrainedSize = NonNegative(device.unzoom_text(self.mScriptUnconstrainedSize.0));
- self.mFont.size = NonNegative(device.unzoom_text(self.mFont.size.0));
- }
-
- pub fn copy_font_size_from(&mut self, other: &Self) {
- self.mScriptUnconstrainedSize = other.mScriptUnconstrainedSize;
-
- self.mSize = other.mScriptUnconstrainedSize;
- // NOTE: Intentionally not copying from mFont.size. The cascade process
- // recomputes the used size as needed.
- self.mFont.size = other.mSize;
- self.mFontSizeKeyword = other.mFontSizeKeyword;
-
- // TODO(emilio): Should we really copy over these two?
- self.mFontSizeFactor = other.mFontSizeFactor;
- self.mFontSizeOffset = other.mFontSizeOffset;
- }
-
- pub fn reset_font_size(&mut self, other: &Self) {
- self.copy_font_size_from(other)
- }
-
- pub fn set_font_size(&mut self, v: FontSize) {
- let computed_size = v.computed_size;
- self.mScriptUnconstrainedSize = computed_size;
-
- // These two may be changed from Cascade::fixup_font_stuff.
- self.mSize = computed_size;
- // NOTE: Intentionally not copying from used_size. The cascade process
- // recomputes the used size as needed.
- self.mFont.size = computed_size;
-
- self.mFontSizeKeyword = v.keyword_info.kw;
- self.mFontSizeFactor = v.keyword_info.factor;
- self.mFontSizeOffset = v.keyword_info.offset;
- }
-
- pub fn clone_font_size(&self) -> FontSize {
- use crate::values::specified::font::KeywordInfo;
-
- FontSize {
- computed_size: self.mSize,
- used_size: self.mFont.size,
- keyword_info: KeywordInfo {
- kw: self.mFontSizeKeyword,
- factor: self.mFontSizeFactor,
- offset: self.mFontSizeOffset,
- }
- }
- }
-
- ${impl_simple('font_weight', 'mFont.weight')}
- ${impl_simple('font_stretch', 'mFont.stretch')}
- ${impl_simple('font_style', 'mFont.style')}
-
- ${impl_simple("font_variant_alternates", "mFont.variantAlternates")}
-
- ${impl_simple("font_size_adjust", "mFont.sizeAdjust")}
-
- ${impl_simple("font_family", "mFont.family")}
-
- #[allow(non_snake_case)]
- pub fn set__x_lang(&mut self, v: longhands::_x_lang::computed_value::T) {
- let ptr = v.0.as_ptr();
- forget(v);
- unsafe {
- Gecko_nsStyleFont_SetLang(&mut **self, ptr);
- }
- }
-
- #[allow(non_snake_case)]
- pub fn copy__x_lang_from(&mut self, other: &Self) {
- unsafe {
- Gecko_nsStyleFont_CopyLangFrom(&mut **self, &**other);
- }
- }
-
- #[allow(non_snake_case)]
- pub fn reset__x_lang(&mut self, other: &Self) {
- self.copy__x_lang_from(other)
- }
-
- #[allow(non_snake_case)]
- pub fn clone__x_lang(&self) -> longhands::_x_lang::computed_value::T {
- longhands::_x_lang::computed_value::T(unsafe {
- Atom::from_raw(self.mLanguage.mRawPtr)
- })
- }
-
-
- ${impl_simple_type_with_conversion("font_language_override", "mFont.languageOverride")}
- ${impl_simple_type_with_conversion("font_variant_ligatures", "mFont.variantLigatures")}
- ${impl_simple_type_with_conversion("font_variant_east_asian", "mFont.variantEastAsian")}
- ${impl_simple_type_with_conversion("font_variant_numeric", "mFont.variantNumeric")}
-
- #[allow(non_snake_case)]
- pub fn clone__moz_min_font_size_ratio(
- &self,
- ) -> longhands::_moz_min_font_size_ratio::computed_value::T {
- Percentage(self.mMinFontSizeRatio as f32 / 100.)
- }
-
- #[allow(non_snake_case)]
- pub fn set__moz_min_font_size_ratio(&mut self, v: longhands::_moz_min_font_size_ratio::computed_value::T) {
- let scaled = v.0 * 100.;
- let percentage = if scaled > 255. {
- 255.
- } else if scaled < 0. {
- 0.
- } else {
- scaled
- };
-
- self.mMinFontSizeRatio = percentage as u8;
- }
-
- ${impl_simple_copy('_moz_min_font_size_ratio', 'mMinFontSizeRatio')}
-</%self:impl_trait>
-
-<%def name="impl_coordinated_property_copy(type, ident, gecko_ffi_name)">
- #[allow(non_snake_case)]
- pub fn copy_${type}_${ident}_from(&mut self, other: &Self) {
- self.m${to_camel_case(type)}s.ensure_len(other.m${to_camel_case(type)}s.len());
-
- let count = other.m${to_camel_case(type)}${gecko_ffi_name}Count;
- self.m${to_camel_case(type)}${gecko_ffi_name}Count = count;
-
- let iter = self.m${to_camel_case(type)}s.iter_mut().take(count as usize).zip(
- other.m${to_camel_case(type)}s.iter()
- );
-
- for (ours, others) in iter {
- ours.m${gecko_ffi_name} = others.m${gecko_ffi_name}.clone();
- }
- }
- #[allow(non_snake_case)]
- pub fn reset_${type}_${ident}(&mut self, other: &Self) {
- self.copy_${type}_${ident}_from(other)
- }
-</%def>
-
-<%def name="impl_coordinated_property_count(type, ident, gecko_ffi_name)">
- #[allow(non_snake_case)]
- pub fn ${type}_${ident}_count(&self) -> usize {
- self.m${to_camel_case(type)}${gecko_ffi_name}Count as usize
- }
-</%def>
-
-<%def name="impl_coordinated_property(type, ident, gecko_ffi_name)">
- #[allow(non_snake_case)]
- pub fn set_${type}_${ident}<I>(&mut self, v: I)
- where
- I: IntoIterator<Item = longhands::${type}_${ident}::computed_value::single_value::T>,
- I::IntoIter: ExactSizeIterator + Clone
- {
- let v = v.into_iter();
- debug_assert_ne!(v.len(), 0);
- let input_len = v.len();
- self.m${to_camel_case(type)}s.ensure_len(input_len);
-
- self.m${to_camel_case(type)}${gecko_ffi_name}Count = input_len as u32;
- for (gecko, servo) in self.m${to_camel_case(type)}s.iter_mut().take(input_len as usize).zip(v) {
- gecko.m${gecko_ffi_name} = servo;
- }
- }
- #[allow(non_snake_case)]
- pub fn ${type}_${ident}_at(&self, index: usize)
- -> longhands::${type}_${ident}::computed_value::SingleComputedValue {
- self.m${to_camel_case(type)}s[index % self.${type}_${ident}_count()].m${gecko_ffi_name}.clone()
- }
- ${impl_coordinated_property_copy(type, ident, gecko_ffi_name)}
- ${impl_coordinated_property_count(type, ident, gecko_ffi_name)}
-</%def>
-
-<%def name="impl_animation_keyword(ident, gecko_ffi_name, keyword, cast_type='u8')">
- #[allow(non_snake_case)]
- pub fn set_animation_${ident}<I>(&mut self, v: I)
- where
- I: IntoIterator<Item = longhands::animation_${ident}::computed_value::single_value::T>,
- I::IntoIter: ExactSizeIterator + Clone
- {
- use crate::properties::longhands::animation_${ident}::single_value::computed_value::T as Keyword;
-
- let v = v.into_iter();
-
- debug_assert_ne!(v.len(), 0);
- let input_len = v.len();
- self.mAnimations.ensure_len(input_len);
-
- self.mAnimation${gecko_ffi_name}Count = input_len as u32;
-
- for (gecko, servo) in self.mAnimations.iter_mut().take(input_len as usize).zip(v) {
- let result = match servo {
- % for value in keyword.values_for("gecko"):
- Keyword::${to_camel_case(value)} =>
- structs::${keyword.gecko_constant(value)} ${keyword.maybe_cast(cast_type)},
- % endfor
- };
- gecko.m${gecko_ffi_name} = result;
- }
- }
- #[allow(non_snake_case)]
- pub fn animation_${ident}_at(&self, index: usize)
- -> longhands::animation_${ident}::computed_value::SingleComputedValue {
- use crate::properties::longhands::animation_${ident}::single_value::computed_value::T as Keyword;
- match self.mAnimations[index].m${gecko_ffi_name} ${keyword.maybe_cast("u32")} {
- % for value in keyword.values_for("gecko"):
- structs::${keyword.gecko_constant(value)} => Keyword::${to_camel_case(value)},
- % endfor
- % if keyword.gecko_inexhaustive:
- _ => panic!("Found unexpected value for animation-${ident}"),
- % endif
- }
- }
- ${impl_coordinated_property_copy('animation', ident, gecko_ffi_name)}
- ${impl_coordinated_property_count('animation', ident, gecko_ffi_name)}
-</%def>
-
-<% skip_box_longhands= """display contain""" %>
-<%self:impl_trait style_struct_name="Box" skip_longhands="${skip_box_longhands}">
- #[inline]
- pub fn set_display(&mut self, v: longhands::display::computed_value::T) {
- self.mDisplay = v;
- self.mOriginalDisplay = v;
- }
-
- #[inline]
- pub fn copy_display_from(&mut self, other: &Self) {
- self.set_display(other.mDisplay);
- }
-
- #[inline]
- pub fn reset_display(&mut self, other: &Self) {
- self.copy_display_from(other)
- }
-
- #[inline]
- pub fn set_adjusted_display(
- &mut self,
- v: longhands::display::computed_value::T,
- _is_item_or_root: bool
- ) {
- self.mDisplay = v;
- }
-
- #[inline]
- pub fn clone_display(&self) -> longhands::display::computed_value::T {
- self.mDisplay
- }
-
- #[inline]
- pub fn set_contain(&mut self, v: longhands::contain::computed_value::T) {
- self.mContain = v;
- self.mEffectiveContainment = v;
- }
-
- #[inline]
- pub fn copy_contain_from(&mut self, other: &Self) {
- self.set_contain(other.mContain);
- }
-
- #[inline]
- pub fn reset_contain(&mut self, other: &Self) {
- self.copy_contain_from(other)
- }
-
- #[inline]
- pub fn clone_contain(&self) -> longhands::contain::computed_value::T {
- self.mContain
- }
-
- #[inline]
- pub fn set_effective_containment(
- &mut self,
- v: longhands::contain::computed_value::T
- ) {
- self.mEffectiveContainment = v;
- }
-
- #[inline]
- pub fn clone_effective_containment(&self) -> longhands::contain::computed_value::T {
- self.mEffectiveContainment
- }
-</%self:impl_trait>
-
-<%def name="simple_image_array_property(name, shorthand, field_name)">
- <%
- image_layers_field = "mImage" if shorthand == "background" else "mMask"
- copy_simple_image_array_property(name, shorthand, image_layers_field, field_name)
- %>
-
- pub fn set_${shorthand}_${name}<I>(&mut self, v: I)
- where I: IntoIterator<Item=longhands::${shorthand}_${name}::computed_value::single_value::T>,
- I::IntoIter: ExactSizeIterator
- {
- use crate::gecko_bindings::structs::nsStyleImageLayers_LayerType as LayerType;
- let v = v.into_iter();
-
- unsafe {
- Gecko_EnsureImageLayersLength(&mut self.${image_layers_field}, v.len(),
- LayerType::${shorthand.title()});
- }
-
- self.${image_layers_field}.${field_name}Count = v.len() as u32;
- for (servo, geckolayer) in v.zip(self.${image_layers_field}.mLayers.iter_mut()) {
- geckolayer.${field_name} = {
- ${caller.body()}
- };
- }
- }
-</%def>
-
-<%def name="copy_simple_image_array_property(name, shorthand, layers_field_name, field_name)">
- pub fn copy_${shorthand}_${name}_from(&mut self, other: &Self) {
- use crate::gecko_bindings::structs::nsStyleImageLayers_LayerType as LayerType;
-
- let count = other.${layers_field_name}.${field_name}Count;
- unsafe {
- Gecko_EnsureImageLayersLength(&mut self.${layers_field_name},
- count as usize,
- LayerType::${shorthand.title()});
- }
- // FIXME(emilio): This may be bogus in the same way as bug 1426246.
- for (layer, other) in self.${layers_field_name}.mLayers.iter_mut()
- .zip(other.${layers_field_name}.mLayers.iter())
- .take(count as usize) {
- layer.${field_name} = other.${field_name}.clone();
- }
- self.${layers_field_name}.${field_name}Count = count;
- }
-
- pub fn reset_${shorthand}_${name}(&mut self, other: &Self) {
- self.copy_${shorthand}_${name}_from(other)
- }
-</%def>
-
-<%def name="impl_simple_image_array_property(name, shorthand, layer_field_name, field_name, struct_name)">
- <%
- ident = "%s_%s" % (shorthand, name)
- style_struct = next(x for x in data.style_structs if x.name == struct_name)
- longhand = next(x for x in style_struct.longhands if x.ident == ident)
- keyword = longhand.keyword
- %>
-
- <% copy_simple_image_array_property(name, shorthand, layer_field_name, field_name) %>
-
- pub fn set_${ident}<I>(&mut self, v: I)
- where
- I: IntoIterator<Item=longhands::${ident}::computed_value::single_value::T>,
- I::IntoIter: ExactSizeIterator,
- {
- use crate::properties::longhands::${ident}::single_value::computed_value::T as Keyword;
- use crate::gecko_bindings::structs::nsStyleImageLayers_LayerType as LayerType;
-
- let v = v.into_iter();
-
- unsafe {
- Gecko_EnsureImageLayersLength(&mut self.${layer_field_name}, v.len(),
- LayerType::${shorthand.title()});
- }
-
- self.${layer_field_name}.${field_name}Count = v.len() as u32;
- for (servo, geckolayer) in v.zip(self.${layer_field_name}.mLayers.iter_mut()) {
- geckolayer.${field_name} = {
- match servo {
- % for value in keyword.values_for("gecko"):
- Keyword::${to_camel_case(value)} =>
- structs::${keyword.gecko_constant(value)} ${keyword.maybe_cast('u8')},
- % endfor
- }
- };
- }
- }
-
- pub fn clone_${ident}(&self) -> longhands::${ident}::computed_value::T {
- use crate::properties::longhands::${ident}::single_value::computed_value::T as Keyword;
-
- % if keyword.needs_cast():
- % for value in keyword.values_for('gecko'):
- const ${keyword.casted_constant_name(value, "u8")} : u8 =
- structs::${keyword.gecko_constant(value)} as u8;
- % endfor
- % endif
-
- longhands::${ident}::computed_value::List(
- self.${layer_field_name}.mLayers.iter()
- .take(self.${layer_field_name}.${field_name}Count as usize)
- .map(|ref layer| {
- match layer.${field_name} {
- % for value in longhand.keyword.values_for("gecko"):
- % if keyword.needs_cast():
- ${keyword.casted_constant_name(value, "u8")}
- % else:
- structs::${keyword.gecko_constant(value)}
- % endif
- => Keyword::${to_camel_case(value)},
- % endfor
- % if keyword.gecko_inexhaustive:
- _ => panic!("Found unexpected value in style struct for ${ident} property"),
- % endif
- }
- }).collect()
- )
- }
-</%def>
-
-<%def name="impl_common_image_layer_properties(shorthand)">
- <%
- if shorthand == "background":
- image_layers_field = "mImage"
- struct_name = "Background"
- else:
- image_layers_field = "mMask"
- struct_name = "SVG"
- %>
-
- <%self:simple_image_array_property name="repeat" shorthand="${shorthand}" field_name="mRepeat">
- use crate::values::specified::background::BackgroundRepeatKeyword;
- use crate::gecko_bindings::structs::nsStyleImageLayers_Repeat;
- use crate::gecko_bindings::structs::StyleImageLayerRepeat;
-
- fn to_ns(repeat: BackgroundRepeatKeyword) -> StyleImageLayerRepeat {
- match repeat {
- BackgroundRepeatKeyword::Repeat => StyleImageLayerRepeat::Repeat,
- BackgroundRepeatKeyword::Space => StyleImageLayerRepeat::Space,
- BackgroundRepeatKeyword::Round => StyleImageLayerRepeat::Round,
- BackgroundRepeatKeyword::NoRepeat => StyleImageLayerRepeat::NoRepeat,
- }
- }
-
- let repeat_x = to_ns(servo.0);
- let repeat_y = to_ns(servo.1);
- nsStyleImageLayers_Repeat {
- mXRepeat: repeat_x,
- mYRepeat: repeat_y,
- }
- </%self:simple_image_array_property>
-
- pub fn clone_${shorthand}_repeat(&self) -> longhands::${shorthand}_repeat::computed_value::T {
- use crate::properties::longhands::${shorthand}_repeat::single_value::computed_value::T;
- use crate::values::specified::background::BackgroundRepeatKeyword;
- use crate::gecko_bindings::structs::StyleImageLayerRepeat;
-
- fn to_servo(repeat: StyleImageLayerRepeat) -> BackgroundRepeatKeyword {
- match repeat {
- StyleImageLayerRepeat::Repeat => BackgroundRepeatKeyword::Repeat,
- StyleImageLayerRepeat::Space => BackgroundRepeatKeyword::Space,
- StyleImageLayerRepeat::Round => BackgroundRepeatKeyword::Round,
- StyleImageLayerRepeat::NoRepeat => BackgroundRepeatKeyword::NoRepeat,
- _ => panic!("Found unexpected value in style struct for ${shorthand}_repeat property"),
- }
- }
-
- longhands::${shorthand}_repeat::computed_value::List(
- self.${image_layers_field}.mLayers.iter()
- .take(self.${image_layers_field}.mRepeatCount as usize)
- .map(|ref layer| {
- T(to_servo(layer.mRepeat.mXRepeat), to_servo(layer.mRepeat.mYRepeat))
- }).collect()
- )
- }
-
- <% impl_simple_image_array_property("clip", shorthand, image_layers_field, "mClip", struct_name) %>
- <% impl_simple_image_array_property("origin", shorthand, image_layers_field, "mOrigin", struct_name) %>
-
- % for (orientation, keyword) in [("x", "horizontal"), ("y", "vertical")]:
- pub fn copy_${shorthand}_position_${orientation}_from(&mut self, other: &Self) {
- use crate::gecko_bindings::structs::nsStyleImageLayers_LayerType as LayerType;
-
- let count = other.${image_layers_field}.mPosition${orientation.upper()}Count;
-
- unsafe {
- Gecko_EnsureImageLayersLength(&mut self.${image_layers_field},
- count as usize,
- LayerType::${shorthand.capitalize()});
- }
-
- for (layer, other) in self.${image_layers_field}.mLayers.iter_mut()
- .zip(other.${image_layers_field}.mLayers.iter())
- .take(count as usize) {
- layer.mPosition.${keyword} = other.mPosition.${keyword}.clone();
- }
- self.${image_layers_field}.mPosition${orientation.upper()}Count = count;
- }
-
- pub fn reset_${shorthand}_position_${orientation}(&mut self, other: &Self) {
- self.copy_${shorthand}_position_${orientation}_from(other)
- }
-
- pub fn clone_${shorthand}_position_${orientation}(&self)
- -> longhands::${shorthand}_position_${orientation}::computed_value::T {
- longhands::${shorthand}_position_${orientation}::computed_value::List(
- self.${image_layers_field}.mLayers.iter()
- .take(self.${image_layers_field}.mPosition${orientation.upper()}Count as usize)
- .map(|position| position.mPosition.${keyword}.clone())
- .collect()
- )
- }
-
- pub fn set_${shorthand}_position_${orientation[0]}<I>(&mut self,
- v: I)
- where I: IntoIterator<Item = longhands::${shorthand}_position_${orientation[0]}
- ::computed_value::single_value::T>,
- I::IntoIter: ExactSizeIterator
- {
- use crate::gecko_bindings::structs::nsStyleImageLayers_LayerType as LayerType;
-
- let v = v.into_iter();
-
- unsafe {
- Gecko_EnsureImageLayersLength(&mut self.${image_layers_field}, v.len(),
- LayerType::${shorthand.capitalize()});
- }
-
- self.${image_layers_field}.mPosition${orientation[0].upper()}Count = v.len() as u32;
- for (servo, geckolayer) in v.zip(self.${image_layers_field}
- .mLayers.iter_mut()) {
- geckolayer.mPosition.${keyword} = servo;
- }
- }
- % endfor
-
- <%self:simple_image_array_property name="size" shorthand="${shorthand}" field_name="mSize">
- servo
- </%self:simple_image_array_property>
-
- pub fn clone_${shorthand}_size(&self) -> longhands::${shorthand}_size::computed_value::T {
- longhands::${shorthand}_size::computed_value::List(
- self.${image_layers_field}.mLayers.iter().map(|layer| layer.mSize.clone()).collect()
- )
- }
-
- pub fn copy_${shorthand}_image_from(&mut self, other: &Self) {
- use crate::gecko_bindings::structs::nsStyleImageLayers_LayerType as LayerType;
- unsafe {
- let count = other.${image_layers_field}.mImageCount;
- Gecko_EnsureImageLayersLength(&mut self.${image_layers_field},
- count as usize,
- LayerType::${shorthand.capitalize()});
-
- for (layer, other) in self.${image_layers_field}.mLayers.iter_mut()
- .zip(other.${image_layers_field}.mLayers.iter())
- .take(count as usize) {
- layer.mImage = other.mImage.clone();
- }
- self.${image_layers_field}.mImageCount = count;
- }
- }
-
- pub fn reset_${shorthand}_image(&mut self, other: &Self) {
- self.copy_${shorthand}_image_from(other)
- }
-
- #[allow(unused_variables)]
- pub fn set_${shorthand}_image<I>(&mut self, images: I)
- where I: IntoIterator<Item = longhands::${shorthand}_image::computed_value::single_value::T>,
- I::IntoIter: ExactSizeIterator
- {
- use crate::gecko_bindings::structs::nsStyleImageLayers_LayerType as LayerType;
-
- let images = images.into_iter();
-
- unsafe {
- Gecko_EnsureImageLayersLength(
- &mut self.${image_layers_field},
- images.len(),
- LayerType::${shorthand.title()},
- );
- }
-
- self.${image_layers_field}.mImageCount = images.len() as u32;
- for (image, geckoimage) in images.zip(self.${image_layers_field}
- .mLayers.iter_mut()) {
- geckoimage.mImage = image;
- }
- }
-
- pub fn clone_${shorthand}_image(&self) -> longhands::${shorthand}_image::computed_value::T {
- longhands::${shorthand}_image::computed_value::List(
- self.${image_layers_field}.mLayers.iter()
- .take(self.${image_layers_field}.mImageCount as usize)
- .map(|layer| layer.mImage.clone())
- .collect()
- )
- }
-
- <%
- fill_fields = "mRepeat mClip mOrigin mPositionX mPositionY mImage mSize"
- if shorthand == "background":
- fill_fields += " mAttachment mBlendMode"
- else:
- # mSourceURI uses mImageCount
- fill_fields += " mMaskMode mComposite"
- %>
- pub fn fill_arrays(&mut self) {
- use crate::gecko_bindings::bindings::Gecko_FillAllImageLayers;
- use std::cmp;
- let mut max_len = 1;
- % for member in fill_fields.split():
- max_len = cmp::max(max_len, self.${image_layers_field}.${member}Count);
- % endfor
- unsafe {
- // While we could do this manually, we'd need to also manually
- // run all the copy constructors, so we just delegate to gecko
- Gecko_FillAllImageLayers(&mut self.${image_layers_field}, max_len);
- }
- }
-</%def>
-
-// TODO: Gecko accepts lists in most background-related properties. We just use
-// the first element (which is the common case), but at some point we want to
-// add support for parsing these lists in servo and pushing to nsTArray's.
-<% skip_background_longhands = """background-repeat
- background-image background-clip
- background-origin background-attachment
- background-size background-position
- background-blend-mode
- background-position-x
- background-position-y""" %>
-<%self:impl_trait style_struct_name="Background"
- skip_longhands="${skip_background_longhands}">
-
- <% impl_common_image_layer_properties("background") %>
- <% impl_simple_image_array_property("attachment", "background", "mImage", "mAttachment", "Background") %>
- <% impl_simple_image_array_property("blend_mode", "background", "mImage", "mBlendMode", "Background") %>
-</%self:impl_trait>
-
-<%self:impl_trait style_struct_name="List" skip_longhands="list-style-type">
- pub fn set_list_style_type(&mut self, v: longhands::list_style_type::computed_value::T) {
- use nsstring::{nsACString, nsCStr};
- use self::longhands::list_style_type::computed_value::T;
- match v {
- T::None => unsafe {
- bindings::Gecko_SetCounterStyleToNone(&mut self.mCounterStyle)
- }
- T::CounterStyle(s) => s.to_gecko_value(&mut self.mCounterStyle),
- T::String(s) => unsafe {
- bindings::Gecko_SetCounterStyleToString(
- &mut self.mCounterStyle,
- &nsCStr::from(&s) as &nsACString,
- )
- }
- }
- }
-
- pub fn copy_list_style_type_from(&mut self, other: &Self) {
- unsafe {
- Gecko_CopyCounterStyle(&mut self.mCounterStyle, &other.mCounterStyle);
- }
- }
-
- pub fn reset_list_style_type(&mut self, other: &Self) {
- self.copy_list_style_type_from(other)
- }
-
- pub fn clone_list_style_type(&self) -> longhands::list_style_type::computed_value::T {
- use self::longhands::list_style_type::computed_value::T;
- use crate::values::Either;
- use crate::values::generics::CounterStyle;
- use crate::gecko_bindings::bindings;
-
- let name = unsafe {
- bindings::Gecko_CounterStyle_GetName(&self.mCounterStyle)
- };
- if !name.is_null() {
- let name = unsafe { Atom::from_raw(name) };
- if name == atom!("none") {
- return T::None;
- }
- }
- let result = CounterStyle::from_gecko_value(&self.mCounterStyle);
- match result {
- Either::First(counter_style) => T::CounterStyle(counter_style),
- Either::Second(string) => T::String(string),
- }
- }
-</%self:impl_trait>
-
-<%self:impl_trait style_struct_name="Table">
-</%self:impl_trait>
-
-<%self:impl_trait style_struct_name="Effects">
-</%self:impl_trait>
-
-<%self:impl_trait style_struct_name="InheritedBox">
-</%self:impl_trait>
-
-<%self:impl_trait style_struct_name="InheritedTable"
- skip_longhands="border-spacing">
-
- pub fn set_border_spacing(&mut self, v: longhands::border_spacing::computed_value::T) {
- self.mBorderSpacingCol = v.horizontal().0;
- self.mBorderSpacingRow = v.vertical().0;
- }
-
- pub fn copy_border_spacing_from(&mut self, other: &Self) {
- self.mBorderSpacingCol = other.mBorderSpacingCol;
- self.mBorderSpacingRow = other.mBorderSpacingRow;
- }
-
- pub fn reset_border_spacing(&mut self, other: &Self) {
- self.copy_border_spacing_from(other)
- }
-
- pub fn clone_border_spacing(&self) -> longhands::border_spacing::computed_value::T {
- longhands::border_spacing::computed_value::T::new(
- Au(self.mBorderSpacingCol).into(),
- Au(self.mBorderSpacingRow).into()
- )
- }
-</%self:impl_trait>
-
-
-<%self:impl_trait style_struct_name="InheritedText">
-</%self:impl_trait>
-
-<%self:impl_trait style_struct_name="Text" skip_longhands="initial-letter">
- pub fn set_initial_letter(&mut self, v: longhands::initial_letter::computed_value::T) {
- use crate::values::generics::text::InitialLetter;
- match v {
- InitialLetter::Normal => {
- self.mInitialLetterSize = 0.;
- self.mInitialLetterSink = 0;
- },
- InitialLetter::Specified(size, sink) => {
- self.mInitialLetterSize = size;
- if let Some(sink) = sink {
- self.mInitialLetterSink = sink;
- } else {
- self.mInitialLetterSink = size.floor() as i32;
- }
- }
- }
- }
-
- pub fn copy_initial_letter_from(&mut self, other: &Self) {
- self.mInitialLetterSize = other.mInitialLetterSize;
- self.mInitialLetterSink = other.mInitialLetterSink;
- }
-
- pub fn reset_initial_letter(&mut self, other: &Self) {
- self.copy_initial_letter_from(other)
- }
-
- pub fn clone_initial_letter(&self) -> longhands::initial_letter::computed_value::T {
- use crate::values::generics::text::InitialLetter;
-
- if self.mInitialLetterSize == 0. && self.mInitialLetterSink == 0 {
- InitialLetter::Normal
- } else if self.mInitialLetterSize.floor() as i32 == self.mInitialLetterSink {
- InitialLetter::Specified(self.mInitialLetterSize, None)
- } else {
- InitialLetter::Specified(self.mInitialLetterSize, Some(self.mInitialLetterSink))
- }
- }
-</%self:impl_trait>
-
-<% skip_svg_longhands = """
-mask-mode mask-repeat mask-clip mask-origin mask-composite mask-position-x mask-position-y mask-size mask-image
-"""
-%>
-<%self:impl_trait style_struct_name="SVG"
- skip_longhands="${skip_svg_longhands}">
- <% impl_common_image_layer_properties("mask") %>
- <% impl_simple_image_array_property("mode", "mask", "mMask", "mMaskMode", "SVG") %>
- <% impl_simple_image_array_property("composite", "mask", "mMask", "mComposite", "SVG") %>
-</%self:impl_trait>
-
-<%self:impl_trait style_struct_name="InheritedSVG">
-</%self:impl_trait>
-
-<%self:impl_trait style_struct_name="InheritedUI">
-</%self:impl_trait>
-
-<%self:impl_trait style_struct_name="Column"
- skip_longhands="column-count column-rule-width column-rule-style">
-
- #[allow(unused_unsafe)]
- pub fn set_column_count(&mut self, v: longhands::column_count::computed_value::T) {
- use crate::gecko_bindings::structs::{nsStyleColumn_kColumnCountAuto, nsStyleColumn_kMaxColumnCount};
-
- self.mColumnCount = match v {
- ColumnCount::Integer(integer) => {
- cmp::min(integer.0 as u32, unsafe { nsStyleColumn_kMaxColumnCount })
- },
- ColumnCount::Auto => nsStyleColumn_kColumnCountAuto
- };
- }
-
- ${impl_simple_copy('column_count', 'mColumnCount')}
-
- pub fn clone_column_count(&self) -> longhands::column_count::computed_value::T {
- use crate::gecko_bindings::structs::{nsStyleColumn_kColumnCountAuto, nsStyleColumn_kMaxColumnCount};
- if self.mColumnCount != nsStyleColumn_kColumnCountAuto {
- debug_assert!(self.mColumnCount >= 1 &&
- self.mColumnCount <= nsStyleColumn_kMaxColumnCount);
- ColumnCount::Integer((self.mColumnCount as i32).into())
- } else {
- ColumnCount::Auto
- }
- }
-
- pub fn set_column_rule_style(&mut self, v: longhands::column_rule_style::computed_value::T) {
- self.mColumnRuleStyle = v;
- // NB: This is needed to correctly handling the initial value of
- // column-rule-width when colun-rule-style changes, see the
- // update_border_${side.ident} comment for more details.
- self.mActualColumnRuleWidth = self.mColumnRuleWidth;
- }
-
- pub fn copy_column_rule_style_from(&mut self, other: &Self) {
- self.set_column_rule_style(other.mColumnRuleStyle);
- }
-
- pub fn reset_column_rule_style(&mut self, other: &Self) {
- self.copy_column_rule_style_from(other)
- }
-
- pub fn clone_column_rule_style(&self) -> longhands::column_rule_style::computed_value::T {
- self.mColumnRuleStyle.clone()
- }
-
- ${impl_border_width("column_rule_width", "mActualColumnRuleWidth", "mColumnRuleWidth")}
-
- pub fn column_rule_has_nonzero_width(&self) -> bool {
- self.mActualColumnRuleWidth != 0
- }
-</%self:impl_trait>
-
-<%self:impl_trait style_struct_name="Counters">
- pub fn ineffective_content_property(&self) -> bool {
- !self.mContent.is_items()
- }
-</%self:impl_trait>
-
-<% skip_ui_longhands = """animation-name animation-delay animation-duration
- animation-direction animation-fill-mode
- animation-play-state animation-iteration-count
- animation-timing-function animation-composition animation-timeline
- transition-duration transition-delay
- transition-timing-function transition-property
- scroll-timeline-name scroll-timeline-axis
- view-timeline-name view-timeline-axis view-timeline-inset""" %>
-
-<%self:impl_trait style_struct_name="UI" skip_longhands="${skip_ui_longhands}">
- ${impl_coordinated_property('transition', 'delay', 'Delay')}
- ${impl_coordinated_property('transition', 'duration', 'Duration')}
- ${impl_coordinated_property('transition', 'timing_function', 'TimingFunction')}
-
- pub fn transition_combined_duration_at(&self, index: usize) -> Time {
- // https://drafts.csswg.org/css-transitions/#transition-combined-duration
- Time::from_seconds(
- self.transition_duration_at(index).seconds().max(0.0) +
- self.transition_delay_at(index).seconds()
- )
- }
-
- pub fn set_transition_property<I>(&mut self, v: I)
- where
- I: IntoIterator<Item = longhands::transition_property::computed_value::single_value::T>,
- I::IntoIter: ExactSizeIterator
- {
- use crate::gecko_bindings::structs::nsCSSPropertyID::eCSSPropertyExtra_no_properties;
- use crate::gecko_bindings::structs::nsCSSPropertyID::eCSSPropertyExtra_variable;
- use crate::gecko_bindings::structs::nsCSSPropertyID::eCSSProperty_UNKNOWN;
-
- let v = v.into_iter();
-
- if v.len() != 0 {
- self.mTransitions.ensure_len(v.len());
- self.mTransitionPropertyCount = v.len() as u32;
- for (servo, gecko) in v.zip(self.mTransitions.iter_mut()) {
- unsafe { gecko.mUnknownProperty.clear() };
-
- match servo {
- TransitionProperty::Unsupported(ident) => {
- gecko.mProperty = eCSSProperty_UNKNOWN;
- gecko.mUnknownProperty.mRawPtr = ident.0.into_addrefed();
- },
- TransitionProperty::Custom(name) => {
- gecko.mProperty = eCSSPropertyExtra_variable;
- gecko.mUnknownProperty.mRawPtr = name.into_addrefed();
- }
- _ => gecko.mProperty = servo.to_nscsspropertyid().unwrap(),
- }
- }
- } else {
- // In gecko |none| is represented by eCSSPropertyExtra_no_properties.
- self.mTransitionPropertyCount = 1;
- self.mTransitions[0].mProperty = eCSSPropertyExtra_no_properties;
- }
- }
-
- /// Returns whether there are any transitions specified.
- pub fn specifies_transitions(&self) -> bool {
- use crate::gecko_bindings::structs::nsCSSPropertyID::eCSSPropertyExtra_all_properties;
- if self.mTransitionPropertyCount == 1 &&
- self.mTransitions[0].mProperty == eCSSPropertyExtra_all_properties &&
- self.transition_combined_duration_at(0).seconds() <= 0.0f32 {
- return false;
- }
-
- self.mTransitionPropertyCount > 0
- }
-
- pub fn transition_property_at(&self, index: usize)
- -> longhands::transition_property::computed_value::SingleComputedValue {
- use crate::gecko_bindings::structs::nsCSSPropertyID::eCSSPropertyExtra_no_properties;
- use crate::gecko_bindings::structs::nsCSSPropertyID::eCSSPropertyExtra_variable;
- use crate::gecko_bindings::structs::nsCSSPropertyID::eCSSProperty_UNKNOWN;
-
- let property = self.mTransitions[index].mProperty;
- if property == eCSSProperty_UNKNOWN {
- let atom = self.mTransitions[index].mUnknownProperty.mRawPtr;
- debug_assert!(!atom.is_null());
- TransitionProperty::Unsupported(CustomIdent(unsafe{
- Atom::from_raw(atom)
- }))
- } else if property == eCSSPropertyExtra_variable {
- let atom = self.mTransitions[index].mUnknownProperty.mRawPtr;
- debug_assert!(!atom.is_null());
- TransitionProperty::Custom(unsafe{
- Atom::from_raw(atom)
- })
- } else if property == eCSSPropertyExtra_no_properties {
- // Actually, we don't expect TransitionProperty::Unsupported also
- // represents "none", but if the caller wants to convert it, it is
- // fine. Please use it carefully.
- //
- // FIXME(emilio): This is a hack, is this reachable?
- TransitionProperty::Unsupported(CustomIdent(atom!("none")))
- } else {
- property.into()
- }
- }
-
- pub fn transition_nscsspropertyid_at(&self, index: usize) -> nsCSSPropertyID {
- self.mTransitions[index].mProperty
- }
-
- pub fn copy_transition_property_from(&mut self, other: &Self) {
- use crate::gecko_bindings::structs::nsCSSPropertyID::eCSSPropertyExtra_variable;
- use crate::gecko_bindings::structs::nsCSSPropertyID::eCSSProperty_UNKNOWN;
- self.mTransitions.ensure_len(other.mTransitions.len());
-
- let count = other.mTransitionPropertyCount;
- self.mTransitionPropertyCount = count;
-
- for (index, transition) in self.mTransitions.iter_mut().enumerate().take(count as usize) {
- transition.mProperty = other.mTransitions[index].mProperty;
- unsafe { transition.mUnknownProperty.clear() };
- if transition.mProperty == eCSSProperty_UNKNOWN ||
- transition.mProperty == eCSSPropertyExtra_variable {
- let atom = other.mTransitions[index].mUnknownProperty.mRawPtr;
- debug_assert!(!atom.is_null());
- transition.mUnknownProperty.mRawPtr = unsafe { Atom::from_raw(atom) }.into_addrefed();
- }
- }
- }
-
- pub fn reset_transition_property(&mut self, other: &Self) {
- self.copy_transition_property_from(other)
- }
-
- ${impl_coordinated_property_count('transition', 'property', 'Property')}
-
- pub fn animations_equals(&self, other: &Self) -> bool {
- return self.mAnimationNameCount == other.mAnimationNameCount
- && self.mAnimationDelayCount == other.mAnimationDelayCount
- && self.mAnimationDirectionCount == other.mAnimationDirectionCount
- && self.mAnimationDurationCount == other.mAnimationDurationCount
- && self.mAnimationFillModeCount == other.mAnimationFillModeCount
- && self.mAnimationIterationCountCount == other.mAnimationIterationCountCount
- && self.mAnimationPlayStateCount == other.mAnimationPlayStateCount
- && self.mAnimationTimingFunctionCount == other.mAnimationTimingFunctionCount
- && self.mAnimationCompositionCount == other.mAnimationCompositionCount
- && self.mAnimationTimelineCount == other.mAnimationTimelineCount
- && unsafe { bindings::Gecko_StyleAnimationsEquals(&self.mAnimations, &other.mAnimations) }
- }
-
- pub fn set_animation_name<I>(&mut self, v: I)
- where
- I: IntoIterator<Item = longhands::animation_name::computed_value::single_value::T>,
- I::IntoIter: ExactSizeIterator
- {
- let v = v.into_iter();
- debug_assert_ne!(v.len(), 0);
- self.mAnimations.ensure_len(v.len());
-
- self.mAnimationNameCount = v.len() as u32;
- for (servo, gecko) in v.zip(self.mAnimations.iter_mut()) {
- let atom = servo.0.as_atom().clone();
- unsafe { bindings::Gecko_SetAnimationName(gecko, atom.into_addrefed()); }
- }
- }
- pub fn animation_name_at(&self, index: usize)
- -> longhands::animation_name::computed_value::SingleComputedValue {
- use crate::properties::longhands::animation_name::single_value::SpecifiedValue as AnimationName;
-
- let atom = self.mAnimations[index].mName.mRawPtr;
- AnimationName(KeyframesName::from_atom(unsafe { Atom::from_raw(atom) }))
- }
- pub fn copy_animation_name_from(&mut self, other: &Self) {
- self.mAnimationNameCount = other.mAnimationNameCount;
- unsafe { bindings::Gecko_CopyAnimationNames(&mut self.mAnimations, &other.mAnimations); }
- }
-
- pub fn reset_animation_name(&mut self, other: &Self) {
- self.copy_animation_name_from(other)
- }
-
- ${impl_coordinated_property_count('animation', 'name', 'Name')}
- ${impl_coordinated_property('animation', 'delay', 'Delay')}
- ${impl_coordinated_property('animation', 'duration', 'Duration')}
- ${impl_animation_keyword('direction', 'Direction',
- data.longhands_by_name["animation-direction"].keyword)}
- ${impl_animation_keyword('fill_mode', 'FillMode',
- data.longhands_by_name["animation-fill-mode"].keyword)}
- ${impl_animation_keyword('play_state', 'PlayState',
- data.longhands_by_name["animation-play-state"].keyword)}
- ${impl_animation_keyword('composition', 'Composition',
- data.longhands_by_name["animation-composition"].keyword)}
- ${impl_coordinated_property('animation', 'iteration_count', 'IterationCount')}
- ${impl_coordinated_property('animation', 'timeline', 'Timeline')}
- ${impl_coordinated_property('animation', 'timing_function', 'TimingFunction')}
-
- ${impl_coordinated_property('scroll_timeline', 'name', 'Name')}
- ${impl_coordinated_property('scroll_timeline', 'axis', 'Axis')}
-
- pub fn scroll_timelines_equals(&self, other: &Self) -> bool {
- self.mScrollTimelineNameCount == other.mScrollTimelineNameCount
- && self.mScrollTimelineAxisCount == other.mScrollTimelineAxisCount
- && unsafe {
- bindings::Gecko_StyleScrollTimelinesEquals(
- &self.mScrollTimelines,
- &other.mScrollTimelines,
- )
- }
- }
-
- ${impl_coordinated_property('view_timeline', 'name', 'Name')}
- ${impl_coordinated_property('view_timeline', 'axis', 'Axis')}
- ${impl_coordinated_property('view_timeline', 'inset', 'Inset')}
-
- pub fn view_timelines_equals(&self, other: &Self) -> bool {
- self.mViewTimelineNameCount == other.mViewTimelineNameCount
- && self.mViewTimelineAxisCount == other.mViewTimelineAxisCount
- && self.mViewTimelineInsetCount == other.mViewTimelineInsetCount
- && unsafe {
- bindings::Gecko_StyleViewTimelinesEquals(
- &self.mViewTimelines,
- &other.mViewTimelines,
- )
- }
- }
-</%self:impl_trait>
-
-<%self:impl_trait style_struct_name="XUL">
-</%self:impl_trait>
-
-% for style_struct in data.style_structs:
-${impl_style_struct(style_struct)}
-% endfor
-
-/// Assert that the initial values set in Gecko style struct constructors
-/// match the values returned by `get_initial_value()` for each longhand.
-#[cfg(feature = "gecko")]
-#[inline]
-pub fn assert_initial_values_match(data: &PerDocumentStyleData) {
- if cfg!(debug_assertions) {
- let data = data.borrow();
- let cv = data.stylist.device().default_computed_values();
- <%
- # Skip properties with initial values that change at computed
- # value time, or whose initial value depends on the document
- # / other prefs.
- SKIPPED = [
- "border-top-width",
- "border-bottom-width",
- "border-left-width",
- "border-right-width",
- "column-rule-width",
- "font-family",
- "font-size",
- "outline-width",
- "color",
- ]
- TO_TEST = [p for p in data.longhands if p.enabled_in != "" and not p.logical and not p.name in SKIPPED]
- %>
- % for property in TO_TEST:
- assert_eq!(
- cv.clone_${property.ident}(),
- longhands::${property.ident}::get_initial_value(),
- concat!(
- "initial value in Gecko style struct for ",
- stringify!(${property.ident}),
- " must match longhands::",
- stringify!(${property.ident}),
- "::get_initial_value()"
- )
- );
- % endfor
- }
-}
diff --git a/components/style/properties/helpers.mako.rs b/components/style/properties/helpers.mako.rs
deleted file mode 100644
index 5e1eea5072f..00000000000
--- a/components/style/properties/helpers.mako.rs
+++ /dev/null
@@ -1,1023 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
-
-<%!
- from data import Keyword, to_rust_ident, to_phys, to_camel_case, SYSTEM_FONT_LONGHANDS
- from data import (LOGICAL_CORNERS, PHYSICAL_CORNERS, LOGICAL_SIDES,
- PHYSICAL_SIDES, LOGICAL_SIZES, LOGICAL_AXES)
-%>
-
-<%def name="predefined_type(name, type, initial_value, parse_method='parse',
- vector=False, initial_specified_value=None,
- allow_quirks='No', allow_empty=False, **kwargs)">
- <%def name="predefined_type_inner(name, type, initial_value, parse_method)">
- #[allow(unused_imports)]
- use app_units::Au;
- #[allow(unused_imports)]
- use crate::values::specified::AllowQuirks;
- #[allow(unused_imports)]
- use crate::Zero;
- #[allow(unused_imports)]
- use smallvec::SmallVec;
- pub use crate::values::specified::${type} as SpecifiedValue;
- pub mod computed_value {
- pub use crate::values::computed::${type} as T;
- }
- % if initial_value:
- #[inline] pub fn get_initial_value() -> computed_value::T { ${initial_value} }
- % endif
- % if initial_specified_value:
- #[inline] pub fn get_initial_specified_value() -> SpecifiedValue { ${initial_specified_value} }
- % endif
- #[allow(unused_variables)]
- #[inline]
- pub fn parse<'i, 't>(
- context: &ParserContext,
- input: &mut Parser<'i, 't>,
- ) -> Result<SpecifiedValue, ParseError<'i>> {
- % if allow_quirks != "No":
- specified::${type}::${parse_method}_quirky(context, input, AllowQuirks::${allow_quirks})
- % elif parse_method != "parse":
- specified::${type}::${parse_method}(context, input)
- % else:
- <specified::${type} as crate::parser::Parse>::parse(context, input)
- % endif
- }
- </%def>
- % if vector:
- <%call
- expr="vector_longhand(name, predefined_type=type, allow_empty=allow_empty or not initial_value, **kwargs)"
- >
- ${predefined_type_inner(name, type, initial_value, parse_method)}
- % if caller:
- ${caller.body()}
- % endif
- </%call>
- % else:
- <%call expr="longhand(name, predefined_type=type, **kwargs)">
- ${predefined_type_inner(name, type, initial_value, parse_method)}
- % if caller:
- ${caller.body()}
- % endif
- </%call>
- % endif
-</%def>
-
-// FIXME (Manishearth): Add computed_value_as_specified argument
-// and handle the empty case correctly
-<%doc>
- To be used in cases where we have a grammar like "<thing> [ , <thing> ]*".
-
- Setting allow_empty to False allows for cases where the vector
- is empty. The grammar for these is usually "none | <thing> [ , <thing> ]*".
- We assume that the default/initial value is an empty vector for these.
- `initial_value` need not be defined for these.
-</%doc>
-
-// The setup here is roughly:
-//
-// * UnderlyingList is the list that is stored in the computed value. This may
-// be a shared ArcSlice if the property is inherited.
-// * UnderlyingOwnedList is the list that is used for animation.
-// * Specified values always use OwnedSlice, since it's more compact.
-// * computed_value::List is just a convenient alias that you can use for the
-// computed value list, since this is in the computed_value module.
-//
-// If simple_vector_bindings is true, then we don't use the complex iterator
-// machinery and set_foo_from, and just compute the value like any other
-// longhand.
-<%def name="vector_longhand(name, animation_value_type=None,
- vector_animation_type=None, allow_empty=False,
- simple_vector_bindings=False,
- separator='Comma',
- **kwargs)">
- <%call expr="longhand(name, animation_value_type=animation_value_type, vector=True,
- simple_vector_bindings=simple_vector_bindings, **kwargs)">
- #[allow(unused_imports)]
- use smallvec::SmallVec;
-
- pub mod single_value {
- #[allow(unused_imports)]
- use cssparser::{Parser, BasicParseError};
- #[allow(unused_imports)]
- use crate::parser::{Parse, ParserContext};
- #[allow(unused_imports)]
- use crate::properties::ShorthandId;
- #[allow(unused_imports)]
- use selectors::parser::SelectorParseErrorKind;
- #[allow(unused_imports)]
- use style_traits::{ParseError, StyleParseErrorKind};
- #[allow(unused_imports)]
- use crate::values::computed::{Context, ToComputedValue};
- #[allow(unused_imports)]
- use crate::values::{computed, specified};
- ${caller.body()}
- }
-
- /// The definition of the computed value for ${name}.
- pub mod computed_value {
- #[allow(unused_imports)]
- use crate::values::animated::ToAnimatedValue;
- #[allow(unused_imports)]
- use crate::values::resolved::ToResolvedValue;
- pub use super::single_value::computed_value as single_value;
- pub use self::single_value::T as SingleComputedValue;
- % if not allow_empty or allow_empty == "NotInitial":
- use smallvec::SmallVec;
- % endif
- use crate::values::computed::ComputedVecIter;
-
- <%
- is_shared_list = allow_empty and allow_empty != "NotInitial" and \
- data.longhands_by_name[name].style_struct.inherited
- %>
-
- // FIXME(emilio): Add an OwnedNonEmptySlice type, and figure out
- // something for transition-name, which is the only remaining user
- // of NotInitial.
- pub type UnderlyingList<T> =
- % if allow_empty and allow_empty != "NotInitial":
- % if data.longhands_by_name[name].style_struct.inherited:
- crate::ArcSlice<T>;
- % else:
- crate::OwnedSlice<T>;
- % endif
- % else:
- SmallVec<[T; 1]>;
- % endif
-
- pub type UnderlyingOwnedList<T> =
- % if allow_empty and allow_empty != "NotInitial":
- crate::OwnedSlice<T>;
- % else:
- SmallVec<[T; 1]>;
- % endif
-
-
- /// The generic type defining the animated and resolved values for
- /// this property.
- ///
- /// Making this type generic allows the compiler to figure out the
- /// animated value for us, instead of having to implement it
- /// manually for every type we care about.
- #[derive(
- Clone,
- Debug,
- MallocSizeOf,
- PartialEq,
- ToAnimatedValue,
- ToResolvedValue,
- ToCss,
- )]
- % if separator == "Comma":
- #[css(comma)]
- % endif
- pub struct OwnedList<T>(
- % if not allow_empty:
- #[css(iterable)]
- % else:
- #[css(if_empty = "none", iterable)]
- % endif
- pub UnderlyingOwnedList<T>,
- );
-
- /// The computed value for this property.
- % if not is_shared_list:
- pub type ComputedList = OwnedList<single_value::T>;
- pub use self::OwnedList as List;
- % else:
- pub use self::ComputedList as List;
-
- #[derive(
- Clone,
- Debug,
- MallocSizeOf,
- PartialEq,
- ToCss,
- )]
- % if separator == "Comma":
- #[css(comma)]
- % endif
- pub struct ComputedList(
- % if not allow_empty:
- #[css(iterable)]
- % else:
- #[css(if_empty = "none", iterable)]
- % endif
- % if is_shared_list:
- #[ignore_malloc_size_of = "Arc"]
- % endif
- pub UnderlyingList<single_value::T>,
- );
-
- type ResolvedList = OwnedList<<single_value::T as ToResolvedValue>::ResolvedValue>;
- impl ToResolvedValue for ComputedList {
- type ResolvedValue = ResolvedList;
-
- fn to_resolved_value(self, context: &crate::values::resolved::Context) -> Self::ResolvedValue {
- OwnedList(
- self.0
- .iter()
- .cloned()
- .map(|v| v.to_resolved_value(context))
- .collect()
- )
- }
-
- fn from_resolved_value(resolved: Self::ResolvedValue) -> Self {
- % if not is_shared_list:
- use std::iter::FromIterator;
- % endif
- let iter =
- resolved.0.into_iter().map(ToResolvedValue::from_resolved_value);
- ComputedList(UnderlyingList::from_iter(iter))
- }
- }
- % endif
-
- % if simple_vector_bindings:
- impl From<ComputedList> for UnderlyingList<single_value::T> {
- #[inline]
- fn from(l: ComputedList) -> Self {
- l.0
- }
- }
- impl From<UnderlyingList<single_value::T>> for ComputedList {
- #[inline]
- fn from(l: UnderlyingList<single_value::T>) -> Self {
- List(l)
- }
- }
- % endif
-
- % if vector_animation_type:
- % if not animation_value_type:
- Sorry, this is stupid but needed for now.
- % endif
-
- use crate::values::animated::{Animate, ToAnimatedZero, Procedure, lists};
- use crate::values::distance::{SquaredDistance, ComputeSquaredDistance};
-
- // FIXME(emilio): For some reason rust thinks that this alias is
- // unused, even though it's clearly used below?
- #[allow(unused)]
- type AnimatedList = OwnedList<<single_value::T as ToAnimatedValue>::AnimatedValue>;
-
- % if is_shared_list:
- impl ToAnimatedValue for ComputedList {
- type AnimatedValue = AnimatedList;
-
- fn to_animated_value(self) -> Self::AnimatedValue {
- OwnedList(
- self.0.iter().map(|v| v.clone().to_animated_value()).collect()
- )
- }
-
- fn from_animated_value(animated: Self::AnimatedValue) -> Self {
- let iter =
- animated.0.into_iter().map(ToAnimatedValue::from_animated_value);
- ComputedList(UnderlyingList::from_iter(iter))
- }
- }
- % endif
-
- impl ToAnimatedZero for AnimatedList {
- fn to_animated_zero(&self) -> Result<Self, ()> { Err(()) }
- }
-
- impl Animate for AnimatedList {
- fn animate(
- &self,
- other: &Self,
- procedure: Procedure,
- ) -> Result<Self, ()> {
- Ok(OwnedList(
- lists::${vector_animation_type}::animate(&self.0, &other.0, procedure)?
- ))
- }
- }
- impl ComputeSquaredDistance for AnimatedList {
- fn compute_squared_distance(
- &self,
- other: &Self,
- ) -> Result<SquaredDistance, ()> {
- lists::${vector_animation_type}::squared_distance(&self.0, &other.0)
- }
- }
- % endif
-
- /// The computed value, effectively a list of single values.
- pub use self::ComputedList as T;
-
- pub type Iter<'a, 'cx, 'cx_a> = ComputedVecIter<'a, 'cx, 'cx_a, super::single_value::SpecifiedValue>;
- }
-
- /// The specified value of ${name}.
- #[derive(Clone, Debug, MallocSizeOf, PartialEq, SpecifiedValueInfo, ToCss, ToShmem)]
- % if separator == "Comma":
- #[css(comma)]
- % endif
- pub struct SpecifiedValue(
- % if not allow_empty:
- #[css(iterable)]
- % else:
- #[css(if_empty = "none", iterable)]
- % endif
- pub crate::OwnedSlice<single_value::SpecifiedValue>,
- );
-
- pub fn get_initial_value() -> computed_value::T {
- % if allow_empty and allow_empty != "NotInitial":
- computed_value::List(Default::default())
- % else:
- let mut v = SmallVec::new();
- v.push(single_value::get_initial_value());
- computed_value::List(v)
- % endif
- }
-
- pub fn parse<'i, 't>(
- context: &ParserContext,
- input: &mut Parser<'i, 't>,
- ) -> Result<SpecifiedValue, ParseError<'i>> {
- use style_traits::Separator;
-
- % if allow_empty:
- if input.try_parse(|input| input.expect_ident_matching("none")).is_ok() {
- return Ok(SpecifiedValue(Default::default()))
- }
- % endif
-
- let v = style_traits::${separator}::parse(input, |parser| {
- single_value::parse(context, parser)
- })?;
- Ok(SpecifiedValue(v.into()))
- }
-
- pub use self::single_value::SpecifiedValue as SingleSpecifiedValue;
-
- % if not simple_vector_bindings and engine == "gecko":
- impl SpecifiedValue {
- fn compute_iter<'a, 'cx, 'cx_a>(
- &'a self,
- context: &'cx Context<'cx_a>,
- ) -> computed_value::Iter<'a, 'cx, 'cx_a> {
- computed_value::Iter::new(context, &self.0)
- }
- }
- % endif
-
- impl ToComputedValue for SpecifiedValue {
- type ComputedValue = computed_value::T;
-
- #[inline]
- fn to_computed_value(&self, context: &Context) -> computed_value::T {
- % if not is_shared_list:
- use std::iter::FromIterator;
- % endif
- computed_value::List(computed_value::UnderlyingList::from_iter(
- self.0.iter().map(|i| i.to_computed_value(context))
- ))
- }
-
- #[inline]
- fn from_computed_value(computed: &computed_value::T) -> Self {
- let iter = computed.0.iter().map(ToComputedValue::from_computed_value);
- SpecifiedValue(iter.collect())
- }
- }
- </%call>
-</%def>
-<%def name="longhand(*args, **kwargs)">
- <%
- property = data.declare_longhand(*args, **kwargs)
- if property is None:
- return ""
- %>
- /// ${property.spec}
- pub mod ${property.ident} {
- #[allow(unused_imports)]
- use cssparser::{Parser, BasicParseError, Token};
- #[allow(unused_imports)]
- use crate::parser::{Parse, ParserContext};
- #[allow(unused_imports)]
- use crate::properties::{UnparsedValue, ShorthandId};
- #[allow(unused_imports)]
- use crate::error_reporting::ParseErrorReporter;
- #[allow(unused_imports)]
- use crate::properties::longhands;
- #[allow(unused_imports)]
- use crate::properties::{LonghandId, LonghandIdSet};
- #[allow(unused_imports)]
- use crate::properties::{CSSWideKeyword, ComputedValues, PropertyDeclaration};
- #[allow(unused_imports)]
- use crate::properties::style_structs;
- #[allow(unused_imports)]
- use selectors::parser::SelectorParseErrorKind;
- #[allow(unused_imports)]
- use servo_arc::Arc;
- #[allow(unused_imports)]
- use style_traits::{ParseError, StyleParseErrorKind};
- #[allow(unused_imports)]
- use crate::values::computed::{Context, ToComputedValue};
- #[allow(unused_imports)]
- use crate::values::{computed, generics, specified};
- #[allow(unused_imports)]
- use crate::Atom;
- ${caller.body()}
- #[allow(unused_variables)]
- pub fn cascade_property(
- declaration: &PropertyDeclaration,
- context: &mut computed::Context,
- ) {
- context.for_non_inherited_property = ${"false" if property.style_struct.inherited else "true"};
- let specified_value = match *declaration {
- PropertyDeclaration::${property.camel_case}(ref value) => value,
- PropertyDeclaration::CSSWideKeyword(ref declaration) => {
- debug_assert_eq!(declaration.id, LonghandId::${property.camel_case});
- match declaration.keyword {
- % if not property.style_struct.inherited:
- CSSWideKeyword::Unset |
- % endif
- CSSWideKeyword::Initial => {
- % if not property.style_struct.inherited:
- debug_assert!(false, "Should be handled in apply_properties");
- % else:
- context.builder.reset_${property.ident}();
- % endif
- },
- % if property.style_struct.inherited:
- CSSWideKeyword::Unset |
- % endif
- CSSWideKeyword::Inherit => {
- % if property.style_struct.inherited:
- debug_assert!(false, "Should be handled in apply_properties");
- % else:
- context.rule_cache_conditions.borrow_mut().set_uncacheable();
- context.builder.inherit_${property.ident}();
- % endif
- }
- CSSWideKeyword::RevertLayer |
- CSSWideKeyword::Revert => unreachable!("Should never get here"),
- }
- return;
- }
- PropertyDeclaration::WithVariables(..) => {
- panic!("variables should already have been substituted")
- }
- _ => panic!("entered the wrong cascade_property() implementation"),
- };
-
- % if property.ident in SYSTEM_FONT_LONGHANDS and engine == "gecko":
- if let Some(sf) = specified_value.get_system() {
- longhands::system_font::resolve_system_font(sf, context);
- }
- % endif
-
- % if not property.style_struct.inherited and property.logical:
- context.rule_cache_conditions.borrow_mut()
- .set_writing_mode_dependency(context.builder.writing_mode);
- % endif
-
- % if property.is_vector and not property.simple_vector_bindings and engine == "gecko":
- // In the case of a vector property we want to pass down an
- // iterator so that this can be computed without allocation.
- //
- // However, computing requires a context, but the style struct
- // being mutated is on the context. We temporarily remove it,
- // mutate it, and then put it back. Vector longhands cannot
- // touch their own style struct whilst computing, else this will
- // panic.
- let mut s =
- context.builder.take_${data.current_style_struct.name_lower}();
- {
- let iter = specified_value.compute_iter(context);
- s.set_${property.ident}(iter);
- }
- context.builder.put_${data.current_style_struct.name_lower}(s);
- % else:
- % if property.boxed:
- let computed = (**specified_value).to_computed_value(context);
- % else:
- let computed = specified_value.to_computed_value(context);
- % endif
- context.builder.set_${property.ident}(computed)
- % endif
- }
-
- pub fn parse_declared<'i, 't>(
- context: &ParserContext,
- input: &mut Parser<'i, 't>,
- ) -> Result<PropertyDeclaration, ParseError<'i>> {
- % if property.allow_quirks != "No":
- parse_quirky(context, input, specified::AllowQuirks::${property.allow_quirks})
- % else:
- parse(context, input)
- % endif
- % if property.boxed:
- .map(Box::new)
- % endif
- .map(PropertyDeclaration::${property.camel_case})
- }
- }
-</%def>
-
-<%def name="gecko_keyword_conversion(keyword, values=None, type='SpecifiedValue', cast_to=None)">
- <%
- if not values:
- values = keyword.values_for(engine)
- maybe_cast = "as %s" % cast_to if cast_to else ""
- const_type = cast_to if cast_to else "u32"
- %>
- #[cfg(feature = "gecko")]
- impl ${type} {
- /// Obtain a specified value from a Gecko keyword value
- ///
- /// Intended for use with presentation attributes, not style structs
- pub fn from_gecko_keyword(kw: u32) -> Self {
- use crate::gecko_bindings::structs;
- % for value in values:
- // We can't match on enum values if we're matching on a u32
- const ${to_rust_ident(value).upper()}: ${const_type}
- = structs::${keyword.gecko_constant(value)} as ${const_type};
- % endfor
- match kw ${maybe_cast} {
- % for value in values:
- ${to_rust_ident(value).upper()} => ${type}::${to_camel_case(value)},
- % endfor
- _ => panic!("Found unexpected value in style struct for ${keyword.name} property"),
- }
- }
- }
-</%def>
-
-<%def name="gecko_bitflags_conversion(bit_map, gecko_bit_prefix, type, kw_type='u8')">
- #[cfg(feature = "gecko")]
- impl ${type} {
- /// Obtain a specified value from a Gecko keyword value
- ///
- /// Intended for use with presentation attributes, not style structs
- pub fn from_gecko_keyword(kw: ${kw_type}) -> Self {
- % for gecko_bit in bit_map.values():
- use crate::gecko_bindings::structs::${gecko_bit_prefix}${gecko_bit};
- % endfor
-
- let mut bits = ${type}::empty();
- % for servo_bit, gecko_bit in bit_map.items():
- if kw & (${gecko_bit_prefix}${gecko_bit} as ${kw_type}) != 0 {
- bits |= ${servo_bit};
- }
- % endfor
- bits
- }
-
- pub fn to_gecko_keyword(self) -> ${kw_type} {
- % for gecko_bit in bit_map.values():
- use crate::gecko_bindings::structs::${gecko_bit_prefix}${gecko_bit};
- % endfor
-
- let mut bits: ${kw_type} = 0;
- // FIXME: if we ensure that the Servo bitflags storage is the same
- // as Gecko's one, we can just copy it.
- % for servo_bit, gecko_bit in bit_map.items():
- if self.contains(${servo_bit}) {
- bits |= ${gecko_bit_prefix}${gecko_bit} as ${kw_type};
- }
- % endfor
- bits
- }
- }
-</%def>
-
-<%def name="single_keyword(name, values, vector=False,
- needs_conversion=False, **kwargs)">
- <%
- keyword_kwargs = {a: kwargs.pop(a, None) for a in [
- 'gecko_constant_prefix',
- 'gecko_enum_prefix',
- 'extra_gecko_values',
- 'extra_servo_values',
- 'gecko_aliases',
- 'servo_aliases',
- 'custom_consts',
- 'gecko_inexhaustive',
- 'gecko_strip_moz_prefix',
- ]}
- %>
-
- <%def name="inner_body(keyword, needs_conversion=False)">
- pub use self::computed_value::T as SpecifiedValue;
- pub mod computed_value {
- #[cfg_attr(feature = "servo", derive(Deserialize, Serialize))]
- #[derive(Clone, Copy, Debug, Eq, FromPrimitive, Hash, MallocSizeOf, Parse, PartialEq, SpecifiedValueInfo, ToComputedValue, ToCss, ToResolvedValue, ToShmem)]
- pub enum T {
- % for variant in keyword.values_for(engine):
- <%
- aliases = []
- for alias, v in keyword.aliases_for(engine).items():
- if variant == v:
- aliases.append(alias)
- %>
- % if aliases:
- #[parse(aliases = "${','.join(sorted(aliases))}")]
- % endif
- ${to_camel_case(variant)},
- % endfor
- }
- }
- #[inline]
- pub fn get_initial_value() -> computed_value::T {
- computed_value::T::${to_camel_case(values.split()[0])}
- }
- #[inline]
- pub fn get_initial_specified_value() -> SpecifiedValue {
- SpecifiedValue::${to_camel_case(values.split()[0])}
- }
- #[inline]
- pub fn parse<'i, 't>(_context: &ParserContext, input: &mut Parser<'i, 't>)
- -> Result<SpecifiedValue, ParseError<'i>> {
- SpecifiedValue::parse(input)
- }
-
- % if needs_conversion:
- <%
- conversion_values = keyword.values_for(engine) + list(keyword.aliases_for(engine).keys())
- %>
- ${gecko_keyword_conversion(keyword, values=conversion_values)}
- % endif
- </%def>
- % if vector:
- <%call expr="vector_longhand(name, keyword=Keyword(name, values, **keyword_kwargs), **kwargs)">
- ${inner_body(Keyword(name, values, **keyword_kwargs))}
- % if caller:
- ${caller.body()}
- % endif
- </%call>
- % else:
- <%call expr="longhand(name, keyword=Keyword(name, values, **keyword_kwargs), **kwargs)">
- ${inner_body(Keyword(name, values, **keyword_kwargs),
- needs_conversion=needs_conversion)}
- % if caller:
- ${caller.body()}
- % endif
- </%call>
- % endif
-</%def>
-
-<%def name="shorthand(name, sub_properties, derive_serialize=False,
- derive_value_info=True, **kwargs)">
-<%
- shorthand = data.declare_shorthand(name, sub_properties.split(), **kwargs)
- # mako doesn't accept non-string value in parameters with <% %> form, so
- # we have to workaround it this way.
- if not isinstance(derive_value_info, bool):
- derive_value_info = eval(derive_value_info)
-%>
- % if shorthand:
- /// ${shorthand.spec}
- pub mod ${shorthand.ident} {
- use cssparser::Parser;
- use crate::parser::ParserContext;
- use crate::properties::{PropertyDeclaration, SourcePropertyDeclaration, MaybeBoxed, longhands};
- #[allow(unused_imports)]
- use selectors::parser::SelectorParseErrorKind;
- #[allow(unused_imports)]
- use std::fmt::{self, Write};
- #[allow(unused_imports)]
- use style_traits::{ParseError, StyleParseErrorKind};
- #[allow(unused_imports)]
- use style_traits::{CssWriter, KeywordsCollectFn, SpecifiedValueInfo, ToCss};
-
- % if derive_value_info:
- #[derive(SpecifiedValueInfo)]
- % endif
- pub struct Longhands {
- % for sub_property in shorthand.sub_properties:
- pub ${sub_property.ident}:
- % if sub_property.boxed:
- Box<
- % endif
- longhands::${sub_property.ident}::SpecifiedValue
- % if sub_property.boxed:
- >
- % endif
- ,
- % endfor
- }
-
- /// Represents a serializable set of all of the longhand properties that
- /// correspond to a shorthand.
- % if derive_serialize:
- #[derive(ToCss)]
- % endif
- pub struct LonghandsToSerialize<'a> {
- % for sub_property in shorthand.sub_properties:
- pub ${sub_property.ident}:
- % if sub_property.may_be_disabled_in(shorthand, engine):
- Option<
- % endif
- &'a longhands::${sub_property.ident}::SpecifiedValue,
- % if sub_property.may_be_disabled_in(shorthand, engine):
- >,
- % endif
- % endfor
- }
-
- impl<'a> LonghandsToSerialize<'a> {
- /// Tries to get a serializable set of longhands given a set of
- /// property declarations.
- pub fn from_iter(iter: impl Iterator<Item = &'a PropertyDeclaration>) -> Result<Self, ()> {
- // Define all of the expected variables that correspond to the shorthand
- % for sub_property in shorthand.sub_properties:
- let mut ${sub_property.ident} =
- None::< &'a longhands::${sub_property.ident}::SpecifiedValue>;
- % endfor
-
- // Attempt to assign the incoming declarations to the expected variables
- for declaration in iter {
- match *declaration {
- % for sub_property in shorthand.sub_properties:
- PropertyDeclaration::${sub_property.camel_case}(ref value) => {
- ${sub_property.ident} = Some(value)
- },
- % endfor
- _ => {}
- };
- }
-
- // If any of the expected variables are missing, return an error
- match (
- % for sub_property in shorthand.sub_properties:
- ${sub_property.ident},
- % endfor
- ) {
-
- (
- % for sub_property in shorthand.sub_properties:
- % if sub_property.may_be_disabled_in(shorthand, engine):
- ${sub_property.ident},
- % else:
- Some(${sub_property.ident}),
- % endif
- % endfor
- ) =>
- Ok(LonghandsToSerialize {
- % for sub_property in shorthand.sub_properties:
- ${sub_property.ident},
- % endfor
- }),
- _ => Err(())
- }
- }
- }
-
- /// Parse the given shorthand and fill the result into the
- /// `declarations` vector.
- pub fn parse_into<'i, 't>(
- declarations: &mut SourcePropertyDeclaration,
- context: &ParserContext,
- input: &mut Parser<'i, 't>,
- ) -> Result<(), ParseError<'i>> {
- #[allow(unused_imports)]
- use crate::properties::{NonCustomPropertyId, LonghandId};
- input.parse_entirely(|input| parse_value(context, input)).map(|longhands| {
- % for sub_property in shorthand.sub_properties:
- % if sub_property.may_be_disabled_in(shorthand, engine):
- if NonCustomPropertyId::from(LonghandId::${sub_property.camel_case})
- .allowed_in_ignoring_rule_type(context) {
- % endif
- declarations.push(PropertyDeclaration::${sub_property.camel_case}(
- longhands.${sub_property.ident}
- ));
- % if sub_property.may_be_disabled_in(shorthand, engine):
- }
- % endif
- % endfor
- })
- }
-
- /// Try to serialize a given shorthand to a string.
- pub fn to_css(declarations: &[&PropertyDeclaration], dest: &mut crate::str::CssStringWriter) -> fmt::Result {
- match LonghandsToSerialize::from_iter(declarations.iter().cloned()) {
- Ok(longhands) => longhands.to_css(&mut CssWriter::new(dest)),
- Err(_) => Ok(())
- }
- }
-
- ${caller.body()}
- }
- % endif
-</%def>
-
-// A shorthand of kind `<property-1> <property-2>?` where both properties have
-// the same type.
-<%def name="two_properties_shorthand(
- name,
- first_property,
- second_property,
- parser_function='crate::parser::Parse::parse',
- **kwargs
-)">
-<%call expr="self.shorthand(name, sub_properties=' '.join([first_property, second_property]), **kwargs)">
- #[allow(unused_imports)]
- use crate::parser::Parse;
- #[allow(unused_imports)]
- use crate::values::specified;
-
- fn parse_value<'i, 't>(
- context: &ParserContext,
- input: &mut Parser<'i, 't>,
- ) -> Result<Longhands, ParseError<'i>> {
- let parse_one = |c: &ParserContext, input: &mut Parser<'i, 't>| -> Result<
- crate::properties::longhands::${to_rust_ident(first_property)}::SpecifiedValue,
- ParseError<'i>
- > {
- ${parser_function}(c, input)
- };
-
- let first = parse_one(context, input)?;
- let second =
- input.try_parse(|input| parse_one(context, input)).unwrap_or_else(|_| first.clone());
- Ok(expanded! {
- ${to_rust_ident(first_property)}: first,
- ${to_rust_ident(second_property)}: second,
- })
- }
-
- impl<'a> ToCss for LonghandsToSerialize<'a> {
- fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result where W: fmt::Write {
- let first = &self.${to_rust_ident(first_property)};
- let second = &self.${to_rust_ident(second_property)};
-
- first.to_css(dest)?;
- if first != second {
- dest.write_char(' ')?;
- second.to_css(dest)?;
- }
- Ok(())
- }
- }
-</%call>
-</%def>
-
-<%def name="four_sides_shorthand(name, sub_property_pattern,
- parser_function='crate::parser::Parse::parse',
- allow_quirks='No', **kwargs)">
- <% sub_properties=' '.join(sub_property_pattern % side for side in PHYSICAL_SIDES) %>
- <%call expr="self.shorthand(name, sub_properties=sub_properties, **kwargs)">
- #[allow(unused_imports)]
- use crate::parser::Parse;
- use crate::values::generics::rect::Rect;
- #[allow(unused_imports)]
- use crate::values::specified;
-
- fn parse_value<'i, 't>(
- context: &ParserContext,
- input: &mut Parser<'i, 't>,
- ) -> Result<Longhands, ParseError<'i>> {
- let rect = Rect::parse_with(context, input, |c, i| -> Result<
- crate::properties::longhands::${to_rust_ident(sub_property_pattern % "top")}::SpecifiedValue,
- ParseError<'i>
- > {
- % if allow_quirks != "No":
- ${parser_function}_quirky(c, i, specified::AllowQuirks::${allow_quirks})
- % else:
- ${parser_function}(c, i)
- % endif
- })?;
- Ok(expanded! {
- % for index, side in enumerate(["top", "right", "bottom", "left"]):
- ${to_rust_ident(sub_property_pattern % side)}: rect.${index},
- % endfor
- })
- }
-
- impl<'a> ToCss for LonghandsToSerialize<'a> {
- fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
- where
- W: Write,
- {
- let rect = Rect::new(
- % for side in ["top", "right", "bottom", "left"]:
- &self.${to_rust_ident(sub_property_pattern % side)},
- % endfor
- );
- rect.to_css(dest)
- }
- }
- </%call>
-</%def>
-
-<%def name="logical_setter_helper(name)">
- <%
- side = None
- size = None
- corner = None
- axis = None
- maybe_side = [s for s in LOGICAL_SIDES if s in name]
- maybe_size = [s for s in LOGICAL_SIZES if s in name]
- maybe_corner = [s for s in LOGICAL_CORNERS if s in name]
- maybe_axis = [s for s in LOGICAL_AXES if name.endswith(s)]
- if len(maybe_side) == 1:
- side = maybe_side[0]
- elif len(maybe_size) == 1:
- size = maybe_size[0]
- elif len(maybe_corner) == 1:
- corner = maybe_corner[0]
- elif len(maybe_axis) == 1:
- axis = maybe_axis[0]
- def phys_ident(side, phy_side):
- return to_rust_ident(to_phys(name, side, phy_side))
- %>
- % if side is not None:
- use crate::logical_geometry::PhysicalSide;
- match wm.${to_rust_ident(side)}_physical_side() {
- % for phy_side in PHYSICAL_SIDES:
- PhysicalSide::${phy_side.title()} => {
- ${caller.inner(physical_ident=phys_ident(side, phy_side))}
- }
- % endfor
- }
- % elif corner is not None:
- use crate::logical_geometry::PhysicalCorner;
- match wm.${to_rust_ident(corner)}_physical_corner() {
- % for phy_corner in PHYSICAL_CORNERS:
- PhysicalCorner::${to_camel_case(phy_corner)} => {
- ${caller.inner(physical_ident=phys_ident(corner, phy_corner))}
- }
- % endfor
- }
- % elif size is not None:
- <%
- # (horizontal, vertical)
- physical_size = ("height", "width")
- if size == "inline-size":
- physical_size = ("width", "height")
- %>
- if wm.is_vertical() {
- ${caller.inner(physical_ident=phys_ident(size, physical_size[1]))}
- } else {
- ${caller.inner(physical_ident=phys_ident(size, physical_size[0]))}
- }
- % elif axis is not None:
- <%
- if axis == "inline":
- me, other = "x", "y"
- else:
- assert(axis == "block")
- me, other = "y", "x"
- %>
- if wm.is_vertical() {
- ${caller.inner(physical_ident=phys_ident(axis, other))}
- } else {
- ${caller.inner(physical_ident=phys_ident(axis, me))}
- }
- % else:
- <% raise Exception("Don't know what to do with logical property %s" % name) %>
- % endif
-</%def>
-
-<%def name="logical_setter(name)">
- /// Set the appropriate physical property for ${name} given a writing mode.
- pub fn set_${to_rust_ident(name)}(&mut self,
- v: longhands::${to_rust_ident(name)}::computed_value::T,
- wm: WritingMode) {
- <%self:logical_setter_helper name="${name}">
- <%def name="inner(physical_ident)">
- self.set_${physical_ident}(v)
- </%def>
- </%self:logical_setter_helper>
- }
-
- /// Copy the appropriate physical property from another struct for ${name}
- /// given a writing mode.
- pub fn copy_${to_rust_ident(name)}_from(&mut self,
- other: &Self,
- wm: WritingMode) {
- <%self:logical_setter_helper name="${name}">
- <%def name="inner(physical_ident)">
- self.copy_${physical_ident}_from(other)
- </%def>
- </%self:logical_setter_helper>
- }
-
- /// Copy the appropriate physical property from another struct for ${name}
- /// given a writing mode.
- pub fn reset_${to_rust_ident(name)}(&mut self,
- other: &Self,
- wm: WritingMode) {
- self.copy_${to_rust_ident(name)}_from(other, wm)
- }
-
- /// Get the computed value for the appropriate physical property for
- /// ${name} given a writing mode.
- pub fn clone_${to_rust_ident(name)}(&self, wm: WritingMode)
- -> longhands::${to_rust_ident(name)}::computed_value::T {
- <%self:logical_setter_helper name="${name}">
- <%def name="inner(physical_ident)">
- self.clone_${physical_ident}()
- </%def>
- </%self:logical_setter_helper>
- }
-</%def>
diff --git a/components/style/properties/helpers/animated_properties.mako.rs b/components/style/properties/helpers/animated_properties.mako.rs
deleted file mode 100644
index d1e28a5f97b..00000000000
--- a/components/style/properties/helpers/animated_properties.mako.rs
+++ /dev/null
@@ -1,716 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
-
-<%namespace name="helpers" file="/helpers.mako.rs" />
-
-<%
- from data import to_idl_name, SYSTEM_FONT_LONGHANDS, to_camel_case
- from itertools import groupby
-%>
-
-#[cfg(feature = "gecko")] use crate::gecko_bindings::structs::nsCSSPropertyID;
-use crate::properties::{CSSWideKeyword, PropertyDeclaration, NonCustomPropertyIterator};
-use crate::properties::longhands;
-use crate::properties::longhands::visibility::computed_value::T as Visibility;
-use crate::properties::LonghandId;
-use servo_arc::Arc;
-use std::ptr;
-use std::mem;
-use fxhash::FxHashMap;
-use super::ComputedValues;
-use crate::values::animated::{Animate, Procedure, ToAnimatedValue, ToAnimatedZero};
-use crate::values::animated::effects::AnimatedFilter;
-#[cfg(feature = "gecko")] use crate::values::computed::TransitionProperty;
-use crate::values::computed::{ClipRect, Context};
-use crate::values::computed::ToComputedValue;
-use crate::values::distance::{ComputeSquaredDistance, SquaredDistance};
-use crate::values::generics::effects::Filter;
-use void::{self, Void};
-
-/// Convert nsCSSPropertyID to TransitionProperty
-#[cfg(feature = "gecko")]
-#[allow(non_upper_case_globals)]
-impl From<nsCSSPropertyID> for TransitionProperty {
- fn from(property: nsCSSPropertyID) -> TransitionProperty {
- use crate::properties::ShorthandId;
- match property {
- % for prop in data.longhands:
- ${prop.nscsspropertyid()} => {
- TransitionProperty::Longhand(LonghandId::${prop.camel_case})
- }
- % endfor
- % for prop in data.shorthands_except_all():
- ${prop.nscsspropertyid()} => {
- TransitionProperty::Shorthand(ShorthandId::${prop.camel_case})
- }
- % endfor
- nsCSSPropertyID::eCSSPropertyExtra_all_properties => {
- TransitionProperty::Shorthand(ShorthandId::All)
- }
- _ => {
- panic!("non-convertible nsCSSPropertyID")
- }
- }
- }
-}
-
-/// A collection of AnimationValue that were composed on an element.
-/// This HashMap stores the values that are the last AnimationValue to be
-/// composed for each TransitionProperty.
-pub type AnimationValueMap = FxHashMap<LonghandId, AnimationValue>;
-
-/// An enum to represent a single computed value belonging to an animated
-/// property in order to be interpolated with another one. When interpolating,
-/// both values need to belong to the same property.
-///
-/// FIXME: We need to add a path for custom properties, but that's trivial after
-/// this (is a similar path to that of PropertyDeclaration).
-#[derive(Debug, MallocSizeOf)]
-#[repr(u16)]
-pub enum AnimationValue {
- % for prop in data.longhands:
- /// `${prop.name}`
- % if prop.animatable and not prop.logical:
- ${prop.camel_case}(${prop.animated_type()}),
- % else:
- ${prop.camel_case}(Void),
- % endif
- % endfor
-}
-
-<%
- animated = []
- unanimated = []
- animated_with_logical = []
- for prop in data.longhands:
- if prop.animatable:
- animated_with_logical.append(prop)
- if prop.animatable and not prop.logical:
- animated.append(prop)
- else:
- unanimated.append(prop)
-%>
-
-#[repr(C)]
-struct AnimationValueVariantRepr<T> {
- tag: u16,
- value: T
-}
-
-impl Clone for AnimationValue {
- #[inline]
- fn clone(&self) -> Self {
- use self::AnimationValue::*;
-
- <%
- [copy, others] = [list(g) for _, g in groupby(animated, key=lambda x: not x.specified_is_copy())]
- %>
-
- let self_tag = unsafe { *(self as *const _ as *const u16) };
- if self_tag <= LonghandId::${copy[-1].camel_case} as u16 {
- #[derive(Clone, Copy)]
- #[repr(u16)]
- enum CopyVariants {
- % for prop in copy:
- _${prop.camel_case}(${prop.animated_type()}),
- % endfor
- }
-
- unsafe {
- let mut out = mem::MaybeUninit::uninit();
- ptr::write(
- out.as_mut_ptr() as *mut CopyVariants,
- *(self as *const _ as *const CopyVariants),
- );
- return out.assume_init();
- }
- }
-
- match *self {
- % for ty, props in groupby(others, key=lambda x: x.animated_type()):
- <% props = list(props) %>
- ${" |\n".join("{}(ref value)".format(prop.camel_case) for prop in props)} => {
- % if len(props) == 1:
- ${props[0].camel_case}(value.clone())
- % else:
- unsafe {
- let mut out = mem::MaybeUninit::uninit();
- ptr::write(
- out.as_mut_ptr() as *mut AnimationValueVariantRepr<${ty}>,
- AnimationValueVariantRepr {
- tag: *(self as *const _ as *const u16),
- value: value.clone(),
- },
- );
- out.assume_init()
- }
- % endif
- }
- % endfor
- _ => unsafe { debug_unreachable!() }
- }
- }
-}
-
-impl PartialEq for AnimationValue {
- #[inline]
- fn eq(&self, other: &Self) -> bool {
- use self::AnimationValue::*;
-
- unsafe {
- let this_tag = *(self as *const _ as *const u16);
- let other_tag = *(other as *const _ as *const u16);
- if this_tag != other_tag {
- return false;
- }
-
- match *self {
- % for ty, props in groupby(animated, key=lambda x: x.animated_type()):
- ${" |\n".join("{}(ref this)".format(prop.camel_case) for prop in props)} => {
- let other_repr =
- &*(other as *const _ as *const AnimationValueVariantRepr<${ty}>);
- *this == other_repr.value
- }
- % endfor
- ${" |\n".join("{}(void)".format(prop.camel_case) for prop in unanimated)} => {
- void::unreachable(void)
- }
- }
- }
- }
-}
-
-impl AnimationValue {
- /// Returns the longhand id this animated value corresponds to.
- #[inline]
- pub fn id(&self) -> LonghandId {
- let id = unsafe { *(self as *const _ as *const LonghandId) };
- debug_assert_eq!(id, match *self {
- % for prop in data.longhands:
- % if prop.animatable and not prop.logical:
- AnimationValue::${prop.camel_case}(..) => LonghandId::${prop.camel_case},
- % else:
- AnimationValue::${prop.camel_case}(void) => void::unreachable(void),
- % endif
- % endfor
- });
- id
- }
-
- /// "Uncompute" this animation value in order to be used inside the CSS
- /// cascade.
- pub fn uncompute(&self) -> PropertyDeclaration {
- use crate::properties::longhands;
- use self::AnimationValue::*;
-
- use super::PropertyDeclarationVariantRepr;
-
- match *self {
- <% keyfunc = lambda x: (x.base_type(), x.specified_type(), x.boxed, x.is_animatable_with_computed_value) %>
- % for (ty, specified, boxed, computed), props in groupby(animated, key=keyfunc):
- <% props = list(props) %>
- ${" |\n".join("{}(ref value)".format(prop.camel_case) for prop in props)} => {
- % if not computed:
- let ref value = ToAnimatedValue::from_animated_value(value.clone());
- % endif
- let value = ${ty}::from_computed_value(&value);
- % if boxed:
- let value = Box::new(value);
- % endif
- % if len(props) == 1:
- PropertyDeclaration::${props[0].camel_case}(value)
- % else:
- unsafe {
- let mut out = mem::MaybeUninit::uninit();
- ptr::write(
- out.as_mut_ptr() as *mut PropertyDeclarationVariantRepr<${specified}>,
- PropertyDeclarationVariantRepr {
- tag: *(self as *const _ as *const u16),
- value,
- },
- );
- out.assume_init()
- }
- % endif
- }
- % endfor
- ${" |\n".join("{}(void)".format(prop.camel_case) for prop in unanimated)} => {
- void::unreachable(void)
- }
- }
- }
-
- /// Construct an AnimationValue from a property declaration.
- pub fn from_declaration(
- decl: &PropertyDeclaration,
- context: &mut Context,
- extra_custom_properties: Option<<&Arc<crate::custom_properties::CustomPropertiesMap>>,
- initial: &ComputedValues,
- ) -> Option<Self> {
- use super::PropertyDeclarationVariantRepr;
-
- <%
- keyfunc = lambda x: (
- x.specified_type(),
- x.animated_type(),
- x.boxed,
- not x.is_animatable_with_computed_value,
- x.style_struct.inherited,
- x.ident in SYSTEM_FONT_LONGHANDS and engine == "gecko",
- )
- %>
-
- let animatable = match *decl {
- % for (specified_ty, ty, boxed, to_animated, inherit, system), props in groupby(animated_with_logical, key=keyfunc):
- ${" |\n".join("PropertyDeclaration::{}(ref value)".format(prop.camel_case) for prop in props)} => {
- let decl_repr = unsafe {
- &*(decl as *const _ as *const PropertyDeclarationVariantRepr<${specified_ty}>)
- };
- let longhand_id = unsafe {
- *(&decl_repr.tag as *const u16 as *const LonghandId)
- };
- context.for_non_inherited_property = ${"false" if inherit else "true"};
- % if system:
- if let Some(sf) = value.get_system() {
- longhands::system_font::resolve_system_font(sf, context)
- }
- % endif
- % if boxed:
- let value = (**value).to_computed_value(context);
- % else:
- let value = value.to_computed_value(context);
- % endif
- % if to_animated:
- let value = value.to_animated_value();
- % endif
-
- unsafe {
- let mut out = mem::MaybeUninit::uninit();
- ptr::write(
- out.as_mut_ptr() as *mut AnimationValueVariantRepr<${ty}>,
- AnimationValueVariantRepr {
- tag: longhand_id.to_physical(context.builder.writing_mode) as u16,
- value,
- },
- );
- out.assume_init()
- }
- }
- % endfor
- PropertyDeclaration::CSSWideKeyword(ref declaration) => {
- match declaration.id {
- // We put all the animatable properties first in the hopes
- // that it might increase match locality.
- % for prop in data.longhands:
- % if prop.animatable:
- LonghandId::${prop.camel_case} => {
- // FIXME(emilio, bug 1533327): I think revert (and
- // revert-layer) handling is not fine here, but what to
- // do instead?
- //
- // Seems we'd need the computed value as if it was
- // revert, somehow. Treating it as `unset` seems fine
- // for now...
- let style_struct = match declaration.keyword {
- % if not prop.style_struct.inherited:
- CSSWideKeyword::Revert |
- CSSWideKeyword::RevertLayer |
- CSSWideKeyword::Unset |
- % endif
- CSSWideKeyword::Initial => {
- initial.get_${prop.style_struct.name_lower}()
- },
- % if prop.style_struct.inherited:
- CSSWideKeyword::Revert |
- CSSWideKeyword::RevertLayer |
- CSSWideKeyword::Unset |
- % endif
- CSSWideKeyword::Inherit => {
- context.builder
- .get_parent_${prop.style_struct.name_lower}()
- },
- };
- let computed = style_struct
- % if prop.logical:
- .clone_${prop.ident}(context.builder.writing_mode);
- % else:
- .clone_${prop.ident}();
- % endif
-
- % if not prop.is_animatable_with_computed_value:
- let computed = computed.to_animated_value();
- % endif
-
- % if prop.logical:
- let wm = context.builder.writing_mode;
- <%helpers:logical_setter_helper name="${prop.name}">
- <%def name="inner(physical_ident)">
- AnimationValue::${to_camel_case(physical_ident)}(computed)
- </%def>
- </%helpers:logical_setter_helper>
- % else:
- AnimationValue::${prop.camel_case}(computed)
- % endif
- },
- % endif
- % endfor
- % for prop in data.longhands:
- % if not prop.animatable:
- LonghandId::${prop.camel_case} => return None,
- % endif
- % endfor
- }
- },
- PropertyDeclaration::WithVariables(ref declaration) => {
- let mut cache = Default::default();
- let substituted = {
- let custom_properties =
- extra_custom_properties.or_else(|| context.style().custom_properties());
-
- declaration.value.substitute_variables(
- declaration.id,
- context.builder.writing_mode,
- custom_properties,
- context.quirks_mode,
- context.device(),
- &mut cache,
- )
- };
- return AnimationValue::from_declaration(
- &substituted,
- context,
- extra_custom_properties,
- initial,
- )
- },
- _ => return None // non animatable properties will get included because of shorthands. ignore.
- };
- Some(animatable)
- }
-
- /// Get an AnimationValue for an AnimatableLonghand from a given computed values.
- pub fn from_computed_values(
- property: LonghandId,
- style: &ComputedValues,
- ) -> Option<Self> {
- let property = property.to_physical(style.writing_mode);
- Some(match property {
- % for prop in data.longhands:
- % if prop.animatable and not prop.logical:
- LonghandId::${prop.camel_case} => {
- let computed = style.clone_${prop.ident}();
- AnimationValue::${prop.camel_case}(
- % if prop.is_animatable_with_computed_value:
- computed
- % else:
- computed.to_animated_value()
- % endif
- )
- }
- % endif
- % endfor
- _ => return None,
- })
- }
-
- /// Update `style` with the value of this `AnimationValue`.
- ///
- /// SERVO ONLY: This doesn't properly handle things like updating 'em' units
- /// when animated font-size.
- #[cfg(feature = "servo")]
- pub fn set_in_style_for_servo(&self, style: &mut ComputedValues) {
- match self {
- % for prop in data.longhands:
- % if prop.animatable and not prop.logical:
- AnimationValue::${prop.camel_case}(ref value) => {
- % if not prop.is_animatable_with_computed_value:
- let value: longhands::${prop.ident}::computed_value::T =
- ToAnimatedValue::from_animated_value(value.clone());
- style.mutate_${prop.style_struct.name_lower}().set_${prop.ident}(value);
- % else:
- style.mutate_${prop.style_struct.name_lower}().set_${prop.ident}(value.clone());
- % endif
- }
- % else:
- AnimationValue::${prop.camel_case}(..) => unreachable!(),
- % endif
- % endfor
- }
- }
-
- /// As above, but a stub for Gecko.
- #[cfg(feature = "gecko")]
- pub fn set_in_style_for_servo(&self, _: &mut ComputedValues) {
- }
-}
-
-fn animate_discrete<T: Clone>(this: &T, other: &T, procedure: Procedure) -> Result<T, ()> {
- if let Procedure::Interpolate { progress } = procedure {
- Ok(if progress < 0.5 { this.clone() } else { other.clone() })
- } else {
- Err(())
- }
-}
-
-impl Animate for AnimationValue {
- fn animate(&self, other: &Self, procedure: Procedure) -> Result<Self, ()> {
- Ok(unsafe {
- use self::AnimationValue::*;
-
- let this_tag = *(self as *const _ as *const u16);
- let other_tag = *(other as *const _ as *const u16);
- if this_tag != other_tag {
- panic!("Unexpected AnimationValue::animate call");
- }
-
- match *self {
- <% keyfunc = lambda x: (x.animated_type(), x.animation_value_type == "discrete") %>
- % for (ty, discrete), props in groupby(animated, key=keyfunc):
- ${" |\n".join("{}(ref this)".format(prop.camel_case) for prop in props)} => {
- let other_repr =
- &*(other as *const _ as *const AnimationValueVariantRepr<${ty}>);
- % if discrete:
- let value = animate_discrete(this, &other_repr.value, procedure)?;
- % else:
- let value = this.animate(&other_repr.value, procedure)?;
- % endif
-
- let mut out = mem::MaybeUninit::uninit();
- ptr::write(
- out.as_mut_ptr() as *mut AnimationValueVariantRepr<${ty}>,
- AnimationValueVariantRepr {
- tag: this_tag,
- value,
- },
- );
- out.assume_init()
- }
- % endfor
- ${" |\n".join("{}(void)".format(prop.camel_case) for prop in unanimated)} => {
- void::unreachable(void)
- }
- }
- })
- }
-}
-
-<%
- nondiscrete = []
- for prop in animated:
- if prop.animation_value_type != "discrete":
- nondiscrete.append(prop)
-%>
-
-impl ComputeSquaredDistance for AnimationValue {
- fn compute_squared_distance(&self, other: &Self) -> Result<SquaredDistance, ()> {
- unsafe {
- use self::AnimationValue::*;
-
- let this_tag = *(self as *const _ as *const u16);
- let other_tag = *(other as *const _ as *const u16);
- if this_tag != other_tag {
- panic!("Unexpected AnimationValue::compute_squared_distance call");
- }
-
- match *self {
- % for ty, props in groupby(nondiscrete, key=lambda x: x.animated_type()):
- ${" |\n".join("{}(ref this)".format(prop.camel_case) for prop in props)} => {
- let other_repr =
- &*(other as *const _ as *const AnimationValueVariantRepr<${ty}>);
-
- this.compute_squared_distance(&other_repr.value)
- }
- % endfor
- _ => Err(()),
- }
- }
- }
-}
-
-impl ToAnimatedZero for AnimationValue {
- #[inline]
- fn to_animated_zero(&self) -> Result<Self, ()> {
- match *self {
- % for prop in data.longhands:
- % if prop.animatable and not prop.logical and prop.animation_value_type != "discrete":
- AnimationValue::${prop.camel_case}(ref base) => {
- Ok(AnimationValue::${prop.camel_case}(base.to_animated_zero()?))
- },
- % endif
- % endfor
- _ => Err(()),
- }
- }
-}
-
-/// <https://drafts.csswg.org/web-animations-1/#animating-visibility>
-impl Animate for Visibility {
- #[inline]
- fn animate(&self, other: &Self, procedure: Procedure) -> Result<Self, ()> {
- match procedure {
- Procedure::Interpolate { .. } => {
- let (this_weight, other_weight) = procedure.weights();
- match (*self, *other) {
- (Visibility::Visible, _) => {
- Ok(if this_weight > 0.0 { *self } else { *other })
- },
- (_, Visibility::Visible) => {
- Ok(if other_weight > 0.0 { *other } else { *self })
- },
- _ => Err(()),
- }
- },
- _ => Err(()),
- }
- }
-}
-
-impl ComputeSquaredDistance for Visibility {
- #[inline]
- fn compute_squared_distance(&self, other: &Self) -> Result<SquaredDistance, ()> {
- Ok(SquaredDistance::from_sqrt(if *self == *other { 0. } else { 1. }))
- }
-}
-
-impl ToAnimatedZero for Visibility {
- #[inline]
- fn to_animated_zero(&self) -> Result<Self, ()> {
- Err(())
- }
-}
-
-/// <https://drafts.csswg.org/css-transitions/#animtype-rect>
-impl Animate for ClipRect {
- #[inline]
- fn animate(&self, other: &Self, procedure: Procedure) -> Result<Self, ()> {
- use crate::values::computed::LengthOrAuto;
- let animate_component = |this: &LengthOrAuto, other: &LengthOrAuto| {
- let result = this.animate(other, procedure)?;
- if let Procedure::Interpolate { .. } = procedure {
- return Ok(result);
- }
- if result.is_auto() {
- // FIXME(emilio): Why? A couple SMIL tests fail without this,
- // but it seems extremely fishy.
- return Err(());
- }
- Ok(result)
- };
-
- Ok(ClipRect {
- top: animate_component(&self.top, &other.top)?,
- right: animate_component(&self.right, &other.right)?,
- bottom: animate_component(&self.bottom, &other.bottom)?,
- left: animate_component(&self.left, &other.left)?,
- })
- }
-}
-
-<%
- FILTER_FUNCTIONS = [ 'Blur', 'Brightness', 'Contrast', 'Grayscale',
- 'HueRotate', 'Invert', 'Opacity', 'Saturate',
- 'Sepia' ]
-%>
-
-/// <https://drafts.fxtf.org/filters/#animation-of-filters>
-impl Animate for AnimatedFilter {
- fn animate(
- &self,
- other: &Self,
- procedure: Procedure,
- ) -> Result<Self, ()> {
- use crate::values::animated::animate_multiplicative_factor;
- match (self, other) {
- % for func in ['Blur', 'DropShadow', 'Grayscale', 'HueRotate', 'Invert', 'Sepia']:
- (&Filter::${func}(ref this), &Filter::${func}(ref other)) => {
- Ok(Filter::${func}(this.animate(other, procedure)?))
- },
- % endfor
- % for func in ['Brightness', 'Contrast', 'Opacity', 'Saturate']:
- (&Filter::${func}(this), &Filter::${func}(other)) => {
- Ok(Filter::${func}(animate_multiplicative_factor(this, other, procedure)?))
- },
- % endfor
- _ => Err(()),
- }
- }
-}
-
-/// <http://dev.w3.org/csswg/css-transforms/#none-transform-animation>
-impl ToAnimatedZero for AnimatedFilter {
- fn to_animated_zero(&self) -> Result<Self, ()> {
- match *self {
- % for func in ['Blur', 'DropShadow', 'Grayscale', 'HueRotate', 'Invert', 'Sepia']:
- Filter::${func}(ref this) => Ok(Filter::${func}(this.to_animated_zero()?)),
- % endfor
- % for func in ['Brightness', 'Contrast', 'Opacity', 'Saturate']:
- Filter::${func}(_) => Ok(Filter::${func}(1.)),
- % endfor
- _ => Err(()),
- }
- }
-}
-
-/// An iterator over all the properties that transition on a given style.
-pub struct TransitionPropertyIterator<'a> {
- style: &'a ComputedValues,
- index_range: core::ops::Range<usize>,
- longhand_iterator: Option<NonCustomPropertyIterator<LonghandId>>,
-}
-
-impl<'a> TransitionPropertyIterator<'a> {
- /// Create a `TransitionPropertyIterator` for the given style.
- pub fn from_style(style: &'a ComputedValues) -> Self {
- Self {
- style,
- index_range: 0..style.get_ui().transition_property_count(),
- longhand_iterator: None,
- }
- }
-}
-
-/// A single iteration of the TransitionPropertyIterator.
-pub struct TransitionPropertyIteration {
- /// The id of the longhand for this property.
- pub longhand_id: LonghandId,
-
- /// The index of this property in the list of transition properties for this
- /// iterator's style.
- pub index: usize,
-}
-
-impl<'a> Iterator for TransitionPropertyIterator<'a> {
- type Item = TransitionPropertyIteration;
-
- fn next(&mut self) -> Option<Self::Item> {
- use crate::values::computed::TransitionProperty;
- loop {
- if let Some(ref mut longhand_iterator) = self.longhand_iterator {
- if let Some(longhand_id) = longhand_iterator.next() {
- return Some(TransitionPropertyIteration {
- longhand_id,
- index: self.index_range.start - 1,
- });
- }
- self.longhand_iterator = None;
- }
-
- let index = self.index_range.next()?;
- match self.style.get_ui().transition_property_at(index) {
- TransitionProperty::Longhand(longhand_id) => {
- return Some(TransitionPropertyIteration {
- longhand_id,
- index,
- })
- }
- // In the other cases, we set up our state so that we are ready to
- // compute the next value of the iterator and then loop (equivalent
- // to calling self.next()).
- TransitionProperty::Shorthand(ref shorthand_id) =>
- self.longhand_iterator = Some(shorthand_id.longhands()),
- TransitionProperty::Custom(..) | TransitionProperty::Unsupported(..) => {}
- }
- }
- }
-}
diff --git a/components/style/properties/longhands/background.mako.rs b/components/style/properties/longhands/background.mako.rs
deleted file mode 100644
index ebc8f91fc13..00000000000
--- a/components/style/properties/longhands/background.mako.rs
+++ /dev/null
@@ -1,116 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
-
-<%namespace name="helpers" file="/helpers.mako.rs" />
-
-<% data.new_style_struct("Background", inherited=False) %>
-
-${helpers.predefined_type(
- "background-color",
- "Color",
- "computed::Color::transparent()",
- engines="gecko servo",
- initial_specified_value="SpecifiedValue::transparent()",
- spec="https://drafts.csswg.org/css-backgrounds/#background-color",
- animation_value_type="AnimatedColor",
- ignored_when_colors_disabled=True,
- allow_quirks="Yes",
- flags="CAN_ANIMATE_ON_COMPOSITOR",
-)}
-
-${helpers.predefined_type(
- "background-image",
- "Image",
- engines="gecko servo",
- initial_value="computed::Image::None",
- initial_specified_value="specified::Image::None",
- spec="https://drafts.csswg.org/css-backgrounds/#the-background-image",
- vector="True",
- animation_value_type="discrete",
- ignored_when_colors_disabled="True",
-)}
-
-% for (axis, direction, initial) in [("x", "Horizontal", "left"), ("y", "Vertical", "top")]:
- ${helpers.predefined_type(
- "background-position-" + axis,
- "position::" + direction + "Position",
- "computed::LengthPercentage::zero_percent()",
- engines="gecko servo",
- initial_specified_value="SpecifiedValue::initial_specified_value()",
- spec="https://drafts.csswg.org/css-backgrounds-4/#propdef-background-position-" + axis,
- animation_value_type="ComputedValue",
- vector=True,
- vector_animation_type="repeatable_list",
- )}
-% endfor
-
-${helpers.predefined_type(
- "background-repeat",
- "BackgroundRepeat",
- "computed::BackgroundRepeat::repeat()",
- engines="gecko servo",
- initial_specified_value="specified::BackgroundRepeat::repeat()",
- animation_value_type="discrete",
- vector=True,
- spec="https://drafts.csswg.org/css-backgrounds/#the-background-repeat",
-)}
-
-${helpers.single_keyword(
- "background-attachment",
- "scroll fixed" + (" local" if engine == "gecko" else ""),
- engines="gecko servo",
- vector=True,
- gecko_enum_prefix="StyleImageLayerAttachment",
- spec="https://drafts.csswg.org/css-backgrounds/#the-background-attachment",
- animation_value_type="discrete",
-)}
-
-${helpers.single_keyword(
- "background-clip",
- "border-box padding-box content-box",
- engines="gecko servo",
- extra_gecko_values="text",
- vector=True, extra_prefixes="webkit",
- gecko_enum_prefix="StyleGeometryBox",
- gecko_inexhaustive=True,
- spec="https://drafts.csswg.org/css-backgrounds/#the-background-clip",
- animation_value_type="discrete",
-)}
-
-${helpers.single_keyword(
- "background-origin",
- "padding-box border-box content-box",
- engines="gecko servo",
- vector=True, extra_prefixes="webkit",
- gecko_enum_prefix="StyleGeometryBox",
- gecko_inexhaustive=True,
- spec="https://drafts.csswg.org/css-backgrounds/#the-background-origin",
- animation_value_type="discrete",
-)}
-
-${helpers.predefined_type(
- "background-size",
- "BackgroundSize",
- engines="gecko servo",
- initial_value="computed::BackgroundSize::auto()",
- initial_specified_value="specified::BackgroundSize::auto()",
- spec="https://drafts.csswg.org/css-backgrounds/#the-background-size",
- vector=True,
- vector_animation_type="repeatable_list",
- animation_value_type="BackgroundSizeList",
- extra_prefixes="webkit")}
-
-// https://drafts.fxtf.org/compositing/#background-blend-mode
-${helpers.single_keyword(
- "background-blend-mode",
- """normal multiply screen overlay darken lighten color-dodge
- color-burn hard-light soft-light difference exclusion hue
- saturation color luminosity""",
- gecko_enum_prefix="StyleBlend",
- vector=True,
- engines="gecko",
- animation_value_type="discrete",
- gecko_inexhaustive=True,
- spec="https://drafts.fxtf.org/compositing/#background-blend-mode",
-)}
diff --git a/components/style/properties/longhands/border.mako.rs b/components/style/properties/longhands/border.mako.rs
deleted file mode 100644
index 5c615a6aafe..00000000000
--- a/components/style/properties/longhands/border.mako.rs
+++ /dev/null
@@ -1,159 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
-
-<%namespace name="helpers" file="/helpers.mako.rs" />
-<% from data import Keyword, Method, ALL_CORNERS, PHYSICAL_SIDES, ALL_SIDES, maybe_moz_logical_alias %>
-
-<% data.new_style_struct("Border", inherited=False,
- additional_methods=[Method("border_" + side + "_has_nonzero_width",
- "bool") for side in ["top", "right", "bottom", "left"]]) %>
-<%
- def maybe_logical_spec(side, kind):
- if side[1]: # if it is logical
- return "https://drafts.csswg.org/css-logical-props/#propdef-border-%s-%s" % (side[0], kind)
- else:
- return "https://drafts.csswg.org/css-backgrounds/#border-%s-%s" % (side[0], kind)
-%>
-% for side in ALL_SIDES:
- <%
- side_name = side[0]
- is_logical = side[1]
- %>
- ${helpers.predefined_type(
- "border-%s-color" % side_name, "Color",
- "computed_value::T::currentcolor()",
- engines="gecko servo",
- aliases=maybe_moz_logical_alias(engine, side, "-moz-border-%s-color"),
- spec=maybe_logical_spec(side, "color"),
- animation_value_type="AnimatedColor",
- logical=is_logical,
- logical_group="border-color",
- allow_quirks="No" if is_logical else "Yes",
- ignored_when_colors_disabled=True,
- )}
-
- ${helpers.predefined_type(
- "border-%s-style" % side_name, "BorderStyle",
- "specified::BorderStyle::None",
- engines="gecko servo",
- aliases=maybe_moz_logical_alias(engine, side, "-moz-border-%s-style"),
- spec=maybe_logical_spec(side, "style"),
- animation_value_type="discrete" if not is_logical else "none",
- logical=is_logical,
- logical_group="border-style",
- )}
-
- ${helpers.predefined_type(
- "border-%s-width" % side_name,
- "BorderSideWidth",
- "app_units::Au::from_px(3)",
- engines="gecko servo",
- aliases=maybe_moz_logical_alias(engine, side, "-moz-border-%s-width"),
- spec=maybe_logical_spec(side, "width"),
- animation_value_type="NonNegativeLength",
- logical=is_logical,
- logical_group="border-width",
- allow_quirks="No" if is_logical else "Yes",
- servo_restyle_damage="reflow rebuild_and_reflow_inline"
- )}
-% endfor
-
-% for corner in ALL_CORNERS:
- <%
- corner_name = corner[0]
- is_logical = corner[1]
- if is_logical:
- prefixes = None
- else:
- prefixes = "webkit"
- %>
- ${helpers.predefined_type(
- "border-%s-radius" % corner_name,
- "BorderCornerRadius",
- "computed::BorderCornerRadius::zero()",
- "parse",
- engines="gecko servo",
- extra_prefixes=prefixes,
- spec=maybe_logical_spec(corner, "radius"),
- boxed=True,
- animation_value_type="BorderCornerRadius",
- logical_group="border-radius",
- logical=is_logical,
- )}
-% endfor
-
-${helpers.single_keyword(
- "box-decoration-break",
- "slice clone",
- engines="gecko",
- gecko_enum_prefix="StyleBoxDecorationBreak",
- spec="https://drafts.csswg.org/css-break/#propdef-box-decoration-break",
- animation_value_type="discrete",
-)}
-
-${helpers.single_keyword(
- "-moz-float-edge",
- "content-box margin-box",
- engines="gecko",
- gecko_ffi_name="mFloatEdge",
- gecko_enum_prefix="StyleFloatEdge",
- spec="Nonstandard (https://developer.mozilla.org/en-US/docs/Web/CSS/-moz-float-edge)",
- animation_value_type="discrete",
-)}
-
-${helpers.predefined_type(
- "border-image-source",
- "Image",
- engines="gecko servo",
- initial_value="computed::Image::None",
- initial_specified_value="specified::Image::None",
- spec="https://drafts.csswg.org/css-backgrounds/#the-background-image",
- vector=False,
- animation_value_type="discrete",
- boxed=engine == "servo",
- ignored_when_colors_disabled=True
-)}
-
-${helpers.predefined_type(
- "border-image-outset",
- "NonNegativeLengthOrNumberRect",
- engines="gecko servo",
- initial_value="generics::rect::Rect::all(computed::NonNegativeLengthOrNumber::zero())",
- initial_specified_value="generics::rect::Rect::all(specified::NonNegativeLengthOrNumber::zero())",
- spec="https://drafts.csswg.org/css-backgrounds/#border-image-outset",
- animation_value_type="NonNegativeLengthOrNumberRect",
- boxed=True,
-)}
-
-${helpers.predefined_type(
- "border-image-repeat",
- "BorderImageRepeat",
- "computed::BorderImageRepeat::stretch()",
- engines="gecko servo",
- initial_specified_value="specified::BorderImageRepeat::stretch()",
- animation_value_type="discrete",
- spec="https://drafts.csswg.org/css-backgrounds/#the-border-image-repeat",
-)}
-
-${helpers.predefined_type(
- "border-image-width",
- "BorderImageWidth",
- engines="gecko servo",
- initial_value="computed::BorderImageWidth::all(computed::BorderImageSideWidth::one())",
- initial_specified_value="specified::BorderImageWidth::all(specified::BorderImageSideWidth::one())",
- spec="https://drafts.csswg.org/css-backgrounds/#border-image-width",
- animation_value_type="BorderImageWidth",
- boxed=True,
-)}
-
-${helpers.predefined_type(
- "border-image-slice",
- "BorderImageSlice",
- engines="gecko servo",
- initial_value="computed::BorderImageSlice::hundred_percent()",
- initial_specified_value="specified::BorderImageSlice::hundred_percent()",
- spec="https://drafts.csswg.org/css-backgrounds/#border-image-slice",
- animation_value_type="BorderImageSlice",
- boxed=True,
-)}
diff --git a/components/style/properties/longhands/box.mako.rs b/components/style/properties/longhands/box.mako.rs
deleted file mode 100644
index 57375f5faa8..00000000000
--- a/components/style/properties/longhands/box.mako.rs
+++ /dev/null
@@ -1,591 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
-
-<%namespace name="helpers" file="/helpers.mako.rs" />
-<% from data import ALL_AXES, Keyword, Method, to_rust_ident, to_camel_case%>
-
-<% data.new_style_struct("Box",
- inherited=False,
- gecko_name="Display") %>
-
-${helpers.predefined_type(
- "display",
- "Display",
- "computed::Display::inline()",
- engines="gecko servo",
- initial_specified_value="specified::Display::inline()",
- animation_value_type="discrete",
- spec="https://drafts.csswg.org/css-display/#propdef-display",
- servo_restyle_damage="rebuild_and_reflow",
-)}
-
-${helpers.single_keyword(
- "-moz-top-layer",
- "none top",
- engines="gecko",
- gecko_enum_prefix="StyleTopLayer",
- gecko_ffi_name="mTopLayer",
- animation_value_type="none",
- enabled_in="ua",
- spec="Internal (not web-exposed)",
-)}
-
-// An internal-only property for elements in a top layer
-// https://fullscreen.spec.whatwg.org/#top-layer
-${helpers.single_keyword(
- "-servo-top-layer",
- "none top",
- engines="servo",
- animation_value_type="none",
- enabled_in="ua",
- spec="Internal (not web-exposed)",
-)}
-
-<%helpers:single_keyword
- name="position"
- values="static absolute relative fixed sticky"
- engines="gecko servo"
- animation_value_type="discrete"
- gecko_enum_prefix="StylePositionProperty"
- spec="https://drafts.csswg.org/css-position/#position-property"
- servo_restyle_damage="rebuild_and_reflow"
->
-impl computed_value::T {
- pub fn is_absolutely_positioned(self) -> bool {
- matches!(self, Self::Absolute | Self::Fixed)
- }
- pub fn is_relative(self) -> bool {
- self == Self::Relative
- }
-}
-</%helpers:single_keyword>
-
-${helpers.predefined_type(
- "float",
- "Float",
- "computed::Float::None",
- engines="gecko servo",
- initial_specified_value="specified::Float::None",
- spec="https://drafts.csswg.org/css-box/#propdef-float",
- animation_value_type="discrete",
- servo_restyle_damage="rebuild_and_reflow",
- gecko_ffi_name="mFloat",
-)}
-
-${helpers.predefined_type(
- "clear",
- "Clear",
- "computed::Clear::None",
- engines="gecko servo",
- animation_value_type="discrete",
- spec="https://drafts.csswg.org/css2/#propdef-clear",
- servo_restyle_damage="rebuild_and_reflow",
-)}
-
-${helpers.predefined_type(
- "vertical-align",
- "VerticalAlign",
- "computed::VerticalAlign::baseline()",
- engines="gecko servo",
- animation_value_type="ComputedValue",
- spec="https://www.w3.org/TR/CSS2/visudet.html#propdef-vertical-align",
- servo_restyle_damage = "reflow",
-)}
-
-${helpers.predefined_type(
- "baseline-source",
- "BaselineSource",
- "computed::BaselineSource::Auto",
- engines="gecko servo-2013",
- animation_value_type="discrete",
- spec="https://drafts.csswg.org/css-inline-3/#baseline-source",
- servo_restyle_damage = "reflow",
-)}
-
-// CSS 2.1, Section 11 - Visual effects
-
-${helpers.single_keyword(
- "-servo-overflow-clip-box",
- "padding-box content-box",
- engines="servo",
- servo_pref="layout.legacy_layout",
- animation_value_type="none",
- enabled_in="ua",
- spec="Internal, not web-exposed, \
- may be standardized in the future (https://developer.mozilla.org/en-US/docs/Web/CSS/overflow-clip-box)",
-)}
-
-
-% for direction in ["inline", "block"]:
- ${helpers.predefined_type(
- "overflow-clip-box-" + direction,
- "OverflowClipBox",
- "computed::OverflowClipBox::PaddingBox",
- engines="gecko",
- enabled_in="ua",
- gecko_pref="layout.css.overflow-clip-box.enabled",
- animation_value_type="discrete",
- spec="Internal, may be standardized in the future: \
- https://developer.mozilla.org/en-US/docs/Web/CSS/overflow-clip-box",
- )}
-% endfor
-
-% for (axis, logical) in ALL_AXES:
- <% full_name = "overflow-{}".format(axis) %>
- ${helpers.predefined_type(
- full_name,
- "Overflow",
- "computed::Overflow::Visible",
- engines="gecko servo",
- logical_group="overflow",
- logical=logical,
- animation_value_type="discrete",
- spec="https://drafts.csswg.org/css-overflow-3/#propdef-{}".format(full_name),
- servo_restyle_damage = "reflow",
- gecko_pref="layout.css.overflow-logical.enabled" if logical else None,
- )}
-% endfor
-
-${helpers.predefined_type(
- "overflow-anchor",
- "OverflowAnchor",
- "computed::OverflowAnchor::Auto",
- engines="gecko",
- initial_specified_value="specified::OverflowAnchor::Auto",
- gecko_pref="layout.css.scroll-anchoring.enabled",
- spec="https://drafts.csswg.org/css-scroll-anchoring/#exclusion-api",
- animation_value_type="discrete",
-)}
-
-<% transform_extra_prefixes = "moz:layout.css.prefixes.transforms webkit" %>
-
-${helpers.predefined_type(
- "transform",
- "Transform",
- "generics::transform::Transform::none()",
- engines="gecko servo",
- extra_prefixes=transform_extra_prefixes,
- animation_value_type="ComputedValue",
- flags="CAN_ANIMATE_ON_COMPOSITOR",
- spec="https://drafts.csswg.org/css-transforms/#propdef-transform",
- servo_restyle_damage="reflow_out_of_flow",
-)}
-
-${helpers.predefined_type(
- "rotate",
- "Rotate",
- "generics::transform::Rotate::None",
- engines="gecko servo",
- animation_value_type="ComputedValue",
- boxed=True,
- flags="CAN_ANIMATE_ON_COMPOSITOR",
- gecko_pref="layout.css.individual-transform.enabled",
- spec="https://drafts.csswg.org/css-transforms-2/#individual-transforms",
- servo_restyle_damage = "reflow_out_of_flow",
-)}
-
-${helpers.predefined_type(
- "scale",
- "Scale",
- "generics::transform::Scale::None",
- engines="gecko servo",
- animation_value_type="ComputedValue",
- boxed=True,
- flags="CAN_ANIMATE_ON_COMPOSITOR",
- gecko_pref="layout.css.individual-transform.enabled",
- spec="https://drafts.csswg.org/css-transforms-2/#individual-transforms",
- servo_restyle_damage = "reflow_out_of_flow",
-)}
-
-${helpers.predefined_type(
- "translate",
- "Translate",
- "generics::transform::Translate::None",
- engines="gecko servo",
- animation_value_type="ComputedValue",
- boxed=True,
- flags="CAN_ANIMATE_ON_COMPOSITOR",
- gecko_pref="layout.css.individual-transform.enabled",
- spec="https://drafts.csswg.org/css-transforms-2/#individual-transforms",
- servo_restyle_damage="reflow_out_of_flow",
-)}
-
-// Motion Path Module Level 1
-${helpers.predefined_type(
- "offset-path",
- "OffsetPath",
- "computed::OffsetPath::none()",
- engines="gecko",
- animation_value_type="ComputedValue",
- gecko_pref="layout.css.motion-path.enabled",
- flags="CAN_ANIMATE_ON_COMPOSITOR",
- spec="https://drafts.fxtf.org/motion-1/#offset-path-property",
- servo_restyle_damage="reflow_out_of_flow"
-)}
-
-// Motion Path Module Level 1
-${helpers.predefined_type(
- "offset-distance",
- "LengthPercentage",
- "computed::LengthPercentage::zero()",
- engines="gecko",
- animation_value_type="ComputedValue",
- gecko_pref="layout.css.motion-path.enabled",
- flags="CAN_ANIMATE_ON_COMPOSITOR",
- spec="https://drafts.fxtf.org/motion-1/#offset-distance-property",
- servo_restyle_damage="reflow_out_of_flow"
-)}
-
-// Motion Path Module Level 1
-${helpers.predefined_type(
- "offset-rotate",
- "OffsetRotate",
- "computed::OffsetRotate::auto()",
- engines="gecko",
- animation_value_type="ComputedValue",
- gecko_pref="layout.css.motion-path.enabled",
- flags="CAN_ANIMATE_ON_COMPOSITOR",
- spec="https://drafts.fxtf.org/motion-1/#offset-rotate-property",
- servo_restyle_damage="reflow_out_of_flow"
-)}
-
-// Motion Path Module Level 1
-${helpers.predefined_type(
- "offset-anchor",
- "PositionOrAuto",
- "computed::PositionOrAuto::auto()",
- engines="gecko",
- animation_value_type="ComputedValue",
- gecko_pref="layout.css.motion-path.enabled",
- flags="CAN_ANIMATE_ON_COMPOSITOR",
- spec="https://drafts.fxtf.org/motion-1/#offset-anchor-property",
- servo_restyle_damage="reflow_out_of_flow",
- boxed=True
-)}
-
-// Motion Path Module Level 1
-${helpers.predefined_type(
- "offset-position",
- "OffsetPosition",
- "computed::OffsetPosition::auto()",
- engines="gecko",
- animation_value_type="ComputedValue",
- gecko_pref="layout.css.motion-path-offset-position.enabled",
- flags="CAN_ANIMATE_ON_COMPOSITOR",
- spec="https://drafts.fxtf.org/motion-1/#offset-position-property",
- servo_restyle_damage="reflow_out_of_flow",
- boxed=True
-)}
-
-// CSSOM View Module
-// https://www.w3.org/TR/cssom-view-1/
-${helpers.single_keyword(
- "scroll-behavior",
- "auto smooth",
- engines="gecko",
- spec="https://drafts.csswg.org/cssom-view/#propdef-scroll-behavior",
- animation_value_type="discrete",
- gecko_enum_prefix="StyleScrollBehavior",
-)}
-
-${helpers.predefined_type(
- "scroll-snap-align",
- "ScrollSnapAlign",
- "computed::ScrollSnapAlign::none()",
- engines="gecko",
- spec="https://drafts.csswg.org/css-scroll-snap-1/#scroll-snap-align",
- animation_value_type="discrete",
-)}
-
-${helpers.predefined_type(
- "scroll-snap-type",
- "ScrollSnapType",
- "computed::ScrollSnapType::none()",
- engines="gecko",
- spec="https://drafts.csswg.org/css-scroll-snap-1/#scroll-snap-type",
- animation_value_type="discrete",
-)}
-
-${helpers.predefined_type(
- "scroll-snap-stop",
- "ScrollSnapStop",
- "computed::ScrollSnapStop::Normal",
- engines="gecko",
- spec="https://drafts.csswg.org/css-scroll-snap-1/#scroll-snap-stop",
- animation_value_type="discrete",
-)}
-
-% for (axis, logical) in ALL_AXES:
- ${helpers.predefined_type(
- "overscroll-behavior-" + axis,
- "OverscrollBehavior",
- "computed::OverscrollBehavior::Auto",
- engines="gecko",
- logical_group="overscroll-behavior",
- logical=logical,
- gecko_pref="layout.css.overscroll-behavior.enabled",
- spec="https://wicg.github.io/overscroll-behavior/#overscroll-behavior-properties",
- animation_value_type="discrete",
- )}
-% endfor
-
-// Compositing and Blending Level 1
-// http://www.w3.org/TR/compositing-1/
-${helpers.single_keyword(
- "isolation",
- "auto isolate",
- engines="gecko",
- spec="https://drafts.fxtf.org/compositing/#isolation",
- gecko_enum_prefix="StyleIsolation",
- animation_value_type="discrete",
-)}
-
-${helpers.predefined_type(
- "break-after",
- "BreakBetween",
- "computed::BreakBetween::Auto",
- engines="gecko",
- spec="https://drafts.csswg.org/css-break/#propdef-break-after",
- animation_value_type="discrete",
-)}
-
-${helpers.predefined_type(
- "break-before",
- "BreakBetween",
- "computed::BreakBetween::Auto",
- engines="gecko",
- spec="https://drafts.csswg.org/css-break/#propdef-break-before",
- animation_value_type="discrete",
-)}
-
-${helpers.predefined_type(
- "break-inside",
- "BreakWithin",
- "computed::BreakWithin::Auto",
- engines="gecko",
- spec="https://drafts.csswg.org/css-break/#propdef-break-inside",
- animation_value_type="discrete",
-)}
-
-// CSS Basic User Interface Module Level 3
-// http://dev.w3.org/csswg/css-ui
-${helpers.predefined_type(
- "resize",
- "Resize",
- "computed::Resize::None",
- engines="gecko",
- animation_value_type="discrete",
- gecko_ffi_name="mResize",
- spec="https://drafts.csswg.org/css-ui/#propdef-resize",
-)}
-
-${helpers.predefined_type(
- "perspective",
- "Perspective",
- "computed::Perspective::none()",
- engines="gecko servo",
- gecko_ffi_name="mChildPerspective",
- spec="https://drafts.csswg.org/css-transforms/#perspective",
- extra_prefixes=transform_extra_prefixes,
- animation_value_type="AnimatedPerspective",
- servo_restyle_damage = "reflow_out_of_flow",
-)}
-
-${helpers.predefined_type(
- "perspective-origin",
- "Position",
- "computed::position::Position::center()",
- engines="gecko servo",
- boxed=True,
- extra_prefixes=transform_extra_prefixes,
- spec="https://drafts.csswg.org/css-transforms-2/#perspective-origin-property",
- animation_value_type="ComputedValue",
- servo_restyle_damage="reflow_out_of_flow"
-)}
-
-${helpers.single_keyword(
- "backface-visibility",
- "visible hidden",
- engines="gecko servo",
- gecko_enum_prefix="StyleBackfaceVisibility",
- spec="https://drafts.csswg.org/css-transforms/#backface-visibility-property",
- extra_prefixes=transform_extra_prefixes,
- animation_value_type="discrete",
-)}
-
-${helpers.single_keyword(
- "transform-box",
- "border-box fill-box view-box",
- engines="gecko",
- gecko_enum_prefix="StyleGeometryBox",
- spec="https://drafts.csswg.org/css-transforms/#transform-box",
- gecko_inexhaustive="True",
- animation_value_type="discrete",
-)}
-
-${helpers.predefined_type(
- "transform-style",
- "TransformStyle",
- "computed::TransformStyle::Flat",
- engines="gecko servo",
- spec="https://drafts.csswg.org/css-transforms-2/#transform-style-property",
- extra_prefixes=transform_extra_prefixes,
- animation_value_type="discrete",
- servo_restyle_damage = "reflow_out_of_flow",
-)}
-
-${helpers.predefined_type(
- "transform-origin",
- "TransformOrigin",
- "computed::TransformOrigin::initial_value()",
- engines="gecko servo",
- animation_value_type="ComputedValue",
- extra_prefixes=transform_extra_prefixes,
- gecko_ffi_name="mTransformOrigin",
- boxed=True,
- spec="https://drafts.csswg.org/css-transforms/#transform-origin-property",
- servo_restyle_damage="reflow_out_of_flow",
-)}
-
-${helpers.predefined_type(
- "contain",
- "Contain",
- "specified::Contain::empty()",
- engines="gecko",
- animation_value_type="none",
- spec="https://drafts.csswg.org/css-contain/#contain-property",
-)}
-
-${helpers.predefined_type(
- "content-visibility",
- "ContentVisibility",
- "computed::ContentVisibility::Visible",
- engines="gecko",
- spec="https://drafts.csswg.org/css-contain/#content-visibility",
- gecko_pref="layout.css.content-visibility.enabled",
- animation_value_type="none",
-)}
-
-${helpers.predefined_type(
- "container-type",
- "ContainerType",
- "computed::ContainerType::Normal",
- engines="gecko servo",
- animation_value_type="none",
- enabled_in="ua",
- gecko_pref="layout.css.container-queries.enabled",
- servo_pref="layout.container-queries.enabled",
- spec="https://drafts.csswg.org/css-contain-3/#container-type",
-)}
-
-${helpers.predefined_type(
- "container-name",
- "ContainerName",
- "computed::ContainerName::none()",
- engines="gecko servo",
- animation_value_type="none",
- enabled_in="ua",
- gecko_pref="layout.css.container-queries.enabled",
- servo_pref="layout.container-queries.enabled",
- spec="https://drafts.csswg.org/css-contain-3/#container-name",
-)}
-
-${helpers.predefined_type(
- "appearance",
- "Appearance",
- "computed::Appearance::None",
- engines="gecko",
- aliases="-moz-appearance -webkit-appearance",
- spec="https://drafts.csswg.org/css-ui-4/#propdef-appearance",
- animation_value_type="discrete",
- gecko_ffi_name="mAppearance",
-)}
-
-// The inherent widget type of an element, selected by specifying
-// `appearance: auto`.
-${helpers.predefined_type(
- "-moz-default-appearance",
- "Appearance",
- "computed::Appearance::None",
- engines="gecko",
- animation_value_type="none",
- spec="Internal (not web-exposed)",
- enabled_in="chrome",
- gecko_ffi_name="mDefaultAppearance",
-)}
-
-${helpers.single_keyword(
- "-moz-orient",
- "inline block horizontal vertical",
- engines="gecko",
- gecko_ffi_name="mOrient",
- gecko_enum_prefix="StyleOrient",
- spec="Nonstandard (https://developer.mozilla.org/en-US/docs/Web/CSS/-moz-orient)",
- animation_value_type="discrete",
-)}
-
-${helpers.predefined_type(
- "will-change",
- "WillChange",
- "computed::WillChange::auto()",
- engines="gecko",
- animation_value_type="none",
- spec="https://drafts.csswg.org/css-will-change/#will-change",
-)}
-
-// The spec issue for the parse_method: https://github.com/w3c/csswg-drafts/issues/4102.
-${helpers.predefined_type(
- "shape-image-threshold",
- "Opacity",
- "0.0",
- engines="gecko",
- animation_value_type="ComputedValue",
- spec="https://drafts.csswg.org/css-shapes/#shape-image-threshold-property",
-)}
-
-${helpers.predefined_type(
- "shape-margin",
- "NonNegativeLengthPercentage",
- "computed::NonNegativeLengthPercentage::zero()",
- engines="gecko",
- animation_value_type="NonNegativeLengthPercentage",
- spec="https://drafts.csswg.org/css-shapes/#shape-margin-property",
-)}
-
-${helpers.predefined_type(
- "shape-outside",
- "basic_shape::ShapeOutside",
- "generics::basic_shape::ShapeOutside::None",
- engines="gecko",
- animation_value_type="basic_shape::ShapeOutside",
- spec="https://drafts.csswg.org/css-shapes/#shape-outside-property",
-)}
-
-${helpers.predefined_type(
- "touch-action",
- "TouchAction",
- "computed::TouchAction::auto()",
- engines="gecko",
- animation_value_type="discrete",
- spec="https://compat.spec.whatwg.org/#touch-action",
-)}
-
-${helpers.predefined_type(
- "-webkit-line-clamp",
- "LineClamp",
- "computed::LineClamp::none()",
- engines="gecko",
- animation_value_type="ComputedValue",
- spec="https://drafts.csswg.org/css-overflow-3/#line-clamp",
-)}
-
-${helpers.predefined_type(
- "scrollbar-gutter",
- "ScrollbarGutter",
- "computed::ScrollbarGutter::AUTO",
- engines="gecko",
- gecko_pref="layout.css.scrollbar-gutter.enabled",
- animation_value_type="discrete",
- spec="https://drafts.csswg.org/css-overflow-3/#scrollbar-gutter-property",
-)}
diff --git a/components/style/properties/longhands/column.mako.rs b/components/style/properties/longhands/column.mako.rs
deleted file mode 100644
index 6ad4c400c22..00000000000
--- a/components/style/properties/longhands/column.mako.rs
+++ /dev/null
@@ -1,82 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
-
-<%namespace name="helpers" file="/helpers.mako.rs" />
-
-<% data.new_style_struct("Column", inherited=False) %>
-
-${helpers.predefined_type(
- "column-width",
- "length::NonNegativeLengthOrAuto",
- "computed::length::NonNegativeLengthOrAuto::auto()",
- engines="gecko servo",
- initial_specified_value="specified::length::NonNegativeLengthOrAuto::auto()",
- animation_value_type="NonNegativeLengthOrAuto",
- servo_pref="layout.columns.enabled",
- spec="https://drafts.csswg.org/css-multicol/#propdef-column-width",
- servo_restyle_damage="rebuild_and_reflow",
-)}
-
-${helpers.predefined_type(
- "column-count",
- "ColumnCount",
- "computed::ColumnCount::auto()",
- engines="gecko servo",
- initial_specified_value="specified::ColumnCount::auto()",
- servo_pref="layout.columns.enabled",
- animation_value_type="AnimatedColumnCount",
- spec="https://drafts.csswg.org/css-multicol/#propdef-column-count",
- servo_restyle_damage="rebuild_and_reflow",
-)}
-
-${helpers.single_keyword(
- "column-fill",
- "balance auto",
- engines="gecko",
- animation_value_type="discrete",
- gecko_enum_prefix="StyleColumnFill",
- spec="https://drafts.csswg.org/css-multicol/#propdef-column-fill",
-)}
-
-${helpers.predefined_type(
- "column-rule-width",
- "BorderSideWidth",
- "app_units::Au::from_px(3)",
- engines="gecko",
- initial_specified_value="specified::BorderSideWidth::medium()",
- spec="https://drafts.csswg.org/css-multicol/#propdef-column-rule-width",
- animation_value_type="NonNegativeLength",
-)}
-
-// https://drafts.csswg.org/css-multicol-1/#crc
-${helpers.predefined_type(
- "column-rule-color",
- "Color",
- "computed_value::T::currentcolor()",
- engines="gecko",
- initial_specified_value="specified::Color::currentcolor()",
- animation_value_type="AnimatedColor",
- ignored_when_colors_disabled=True,
- spec="https://drafts.csswg.org/css-multicol/#propdef-column-rule-color",
-)}
-
-${helpers.single_keyword(
- "column-span",
- "none all",
- engines="gecko servo",
- servo_pref="layout.columns.enabled",
- animation_value_type="discrete",
- gecko_enum_prefix="StyleColumnSpan",
- spec="https://drafts.csswg.org/css-multicol/#propdef-column-span",
-)}
-
-${helpers.predefined_type(
- "column-rule-style",
- "BorderStyle",
- "computed::BorderStyle::None",
- engines="gecko",
- initial_specified_value="specified::BorderStyle::None",
- animation_value_type="discrete",
- spec="https://drafts.csswg.org/css-multicol/#propdef-column-rule-style",
-)}
diff --git a/components/style/properties/longhands/counters.mako.rs b/components/style/properties/longhands/counters.mako.rs
deleted file mode 100644
index 8cea66bfe8b..00000000000
--- a/components/style/properties/longhands/counters.mako.rs
+++ /dev/null
@@ -1,50 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
-
-<%namespace name="helpers" file="/helpers.mako.rs" />
-
-<% data.new_style_struct("Counters", inherited=False, gecko_name="Content") %>
-
-${helpers.predefined_type(
- "content",
- "Content",
- "computed::Content::normal()",
- engines="gecko servo",
- initial_specified_value="specified::Content::normal()",
- animation_value_type="discrete",
- spec="https://drafts.csswg.org/css-content/#propdef-content",
- servo_restyle_damage="rebuild_and_reflow",
-)}
-
-${helpers.predefined_type(
- "counter-increment",
- "CounterIncrement",
- engines="gecko servo",
- servo_pref="layout.legacy_layout",
- initial_value="Default::default()",
- animation_value_type="discrete",
- spec="https://drafts.csswg.org/css-lists/#propdef-counter-increment",
- servo_restyle_damage="rebuild_and_reflow",
-)}
-
-${helpers.predefined_type(
- "counter-reset",
- "CounterReset",
- engines="gecko servo",
- servo_pref="layout.legacy_layout",
- initial_value="Default::default()",
- animation_value_type="discrete",
- spec="https://drafts.csswg.org/css-lists-3/#propdef-counter-reset",
- servo_restyle_damage="rebuild_and_reflow",
-)}
-
-${helpers.predefined_type(
- "counter-set",
- "CounterSet",
- engines="gecko",
- initial_value="Default::default()",
- animation_value_type="discrete",
- spec="https://drafts.csswg.org/css-lists-3/#propdef-counter-set",
- servo_restyle_damage="rebuild_and_reflow",
-)}
diff --git a/components/style/properties/longhands/effects.mako.rs b/components/style/properties/longhands/effects.mako.rs
deleted file mode 100644
index 263189fdbf0..00000000000
--- a/components/style/properties/longhands/effects.mako.rs
+++ /dev/null
@@ -1,86 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
-
-<%namespace name="helpers" file="/helpers.mako.rs" />
-
-// Box-shadow, etc.
-<% data.new_style_struct("Effects", inherited=False) %>
-
-${helpers.predefined_type(
- "opacity",
- "Opacity",
- "1.0",
- engines="gecko servo",
- animation_value_type="ComputedValue",
- flags="CAN_ANIMATE_ON_COMPOSITOR",
- spec="https://drafts.csswg.org/css-color/#transparency",
- servo_restyle_damage = "reflow_out_of_flow",
-)}
-
-${helpers.predefined_type(
- "box-shadow",
- "BoxShadow",
- None,
- engines="gecko servo",
- servo_pref="layout.legacy_layout",
- vector=True,
- simple_vector_bindings=True,
- animation_value_type="AnimatedBoxShadowList",
- vector_animation_type="with_zero",
- extra_prefixes="webkit",
- ignored_when_colors_disabled=True,
- spec="https://drafts.csswg.org/css-backgrounds/#box-shadow",
-)}
-
-${helpers.predefined_type(
- "clip",
- "ClipRectOrAuto",
- "computed::ClipRectOrAuto::auto()",
- engines="gecko servo",
- animation_value_type="ComputedValue",
- boxed=True,
- allow_quirks="Yes",
- spec="https://drafts.fxtf.org/css-masking/#clip-property",
-)}
-
-${helpers.predefined_type(
- "filter",
- "Filter",
- None,
- engines="gecko servo",
- vector=True,
- simple_vector_bindings=True,
- gecko_ffi_name="mFilters",
- separator="Space",
- animation_value_type="AnimatedFilterList",
- vector_animation_type="with_zero",
- extra_prefixes="webkit",
- spec="https://drafts.fxtf.org/filters/#propdef-filter",
-)}
-
-${helpers.predefined_type(
- "backdrop-filter",
- "Filter",
- None,
- engines="gecko",
- vector=True,
- simple_vector_bindings=True,
- gecko_ffi_name="mBackdropFilters",
- separator="Space",
- animation_value_type="AnimatedFilterList",
- vector_animation_type="with_zero",
- gecko_pref="layout.css.backdrop-filter.enabled",
- spec="https://drafts.fxtf.org/filter-effects-2/#propdef-backdrop-filter",
-)}
-
-${helpers.single_keyword(
- "mix-blend-mode",
- """normal multiply screen overlay darken lighten color-dodge
- color-burn hard-light soft-light difference exclusion hue
- saturation color luminosity""" + ("plus-lighter" if engine == 'gecko' else ""),
- engines="gecko servo",
- gecko_enum_prefix="StyleBlend",
- animation_value_type="discrete",
- spec="https://drafts.fxtf.org/compositing/#propdef-mix-blend-mode",
-)}
diff --git a/components/style/properties/longhands/font.mako.rs b/components/style/properties/longhands/font.mako.rs
deleted file mode 100644
index 91eb872592b..00000000000
--- a/components/style/properties/longhands/font.mako.rs
+++ /dev/null
@@ -1,488 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
-
-<%namespace name="helpers" file="/helpers.mako.rs" />
-<% from data import Method, to_camel_case, to_rust_ident, to_camel_case_lower, SYSTEM_FONT_LONGHANDS %>
-
-<% data.new_style_struct("Font", inherited=True) %>
-
-${helpers.predefined_type(
- "font-family",
- "FontFamily",
- engines="gecko servo",
- initial_value="computed::FontFamily::serif()",
- animation_value_type="discrete",
- spec="https://drafts.csswg.org/css-fonts/#propdef-font-family",
- servo_restyle_damage="rebuild_and_reflow",
-)}
-
-${helpers.predefined_type(
- "font-style",
- "FontStyle",
- engines="gecko servo",
- initial_value="computed::FontStyle::normal()",
- initial_specified_value="specified::FontStyle::normal()",
- animation_value_type="FontStyle",
- spec="https://drafts.csswg.org/css-fonts/#propdef-font-style",
- servo_restyle_damage="rebuild_and_reflow",
-)}
-
-<% font_variant_caps_custom_consts= { "small-caps": "SMALLCAPS",
- "all-small-caps": "ALLSMALL",
- "petite-caps": "PETITECAPS",
- "all-petite-caps": "ALLPETITE",
- "titling-caps": "TITLING" } %>
-
-${helpers.single_keyword(
- "font-variant-caps",
- "normal small-caps",
- engines="gecko servo",
- extra_gecko_values="all-small-caps petite-caps all-petite-caps unicase titling-caps",
- gecko_constant_prefix="NS_FONT_VARIANT_CAPS",
- gecko_ffi_name="mFont.variantCaps",
- spec="https://drafts.csswg.org/css-fonts/#propdef-font-variant-caps",
- custom_consts=font_variant_caps_custom_consts,
- animation_value_type="discrete",
- servo_restyle_damage="rebuild_and_reflow",
-)}
-
-${helpers.predefined_type(
- "font-weight",
- "FontWeight",
- engines="gecko servo",
- initial_value="computed::FontWeight::normal()",
- initial_specified_value="specified::FontWeight::normal()",
- animation_value_type="Number",
- spec="https://drafts.csswg.org/css-fonts/#propdef-font-weight",
- servo_restyle_damage="rebuild_and_reflow",
-)}
-
-${helpers.predefined_type(
- "font-size",
- "FontSize",
- engines="gecko servo",
- initial_value="computed::FontSize::medium()",
- initial_specified_value="specified::FontSize::medium()",
- animation_value_type="NonNegativeLength",
- allow_quirks="Yes",
- spec="https://drafts.csswg.org/css-fonts/#propdef-font-size",
- servo_restyle_damage="rebuild_and_reflow",
-)}
-
-${helpers.predefined_type(
- "font-size-adjust",
- "FontSizeAdjust",
- engines="gecko",
- initial_value="computed::FontSizeAdjust::None",
- initial_specified_value="specified::FontSizeAdjust::None",
- animation_value_type="FontSizeAdjust",
- spec="https://drafts.csswg.org/css-fonts/#propdef-font-size-adjust",
-)}
-
-${helpers.predefined_type(
- "font-synthesis-weight",
- "FontSynthesis",
- engines="gecko",
- initial_value="computed::FontSynthesis::Auto",
- initial_specified_value="specified::FontSynthesis::Auto",
- gecko_ffi_name="mFont.synthesisWeight",
- animation_value_type="discrete",
- spec="https://drafts.csswg.org/css-fonts-4/#font-synthesis-weight",
-)}
-
-${helpers.predefined_type(
- "font-synthesis-style",
- "FontSynthesis",
- engines="gecko",
- initial_value="computed::FontSynthesis::Auto",
- initial_specified_value="specified::FontSynthesis::Auto",
- gecko_ffi_name="mFont.synthesisStyle",
- animation_value_type="discrete",
- spec="https://drafts.csswg.org/css-fonts-4/#font-synthesis-style",
-)}
-
-${helpers.predefined_type(
- "font-synthesis-small-caps",
- "FontSynthesis",
- engines="gecko",
- initial_value="computed::FontSynthesis::Auto",
- initial_specified_value="specified::FontSynthesis::Auto",
- gecko_ffi_name="mFont.synthesisSmallCaps",
- animation_value_type="discrete",
- spec="https://drafts.csswg.org/css-fonts-4/#font-synthesis-small-caps",
-)}
-
-${helpers.predefined_type(
- "font-stretch",
- "FontStretch",
- engines="gecko servo",
- initial_value="computed::FontStretch::hundred()",
- initial_specified_value="specified::FontStretch::normal()",
- animation_value_type="Percentage",
- spec="https://drafts.csswg.org/css-fonts/#propdef-font-stretch",
- servo_restyle_damage="rebuild_and_reflow",
-)}
-
-${helpers.single_keyword(
- "font-kerning",
- "auto none normal",
- engines="gecko",
- gecko_ffi_name="mFont.kerning",
- gecko_constant_prefix="NS_FONT_KERNING",
- spec="https://drafts.csswg.org/css-fonts/#propdef-font-kerning",
- animation_value_type="discrete",
-)}
-
-${helpers.predefined_type(
- "font-variant-alternates",
- "FontVariantAlternates",
- engines="gecko",
- initial_value="computed::FontVariantAlternates::default()",
- initial_specified_value="specified::FontVariantAlternates::default()",
- animation_value_type="discrete",
- spec="https://drafts.csswg.org/css-fonts/#propdef-font-variant-alternates",
-)}
-
-${helpers.predefined_type(
- "font-variant-east-asian",
- "FontVariantEastAsian",
- engines="gecko",
- initial_value="computed::FontVariantEastAsian::empty()",
- initial_specified_value="specified::FontVariantEastAsian::empty()",
- animation_value_type="discrete",
- spec="https://drafts.csswg.org/css-fonts/#propdef-font-variant-east-asian",
-)}
-
-${helpers.single_keyword(
- "font-variant-emoji",
- "normal text emoji unicode",
- engines="gecko",
- gecko_pref="layout.css.font-variant-emoji.enabled",
- has_effect_on_gecko_scrollbars=False,
- gecko_enum_prefix="StyleFontVariantEmoji",
- gecko_ffi_name="mFont.variantEmoji",
- spec="https://drafts.csswg.org/css-fonts/#propdef-font-variant-emoji",
- animation_value_type="discrete",
-)}
-
-${helpers.predefined_type(
- "font-variant-ligatures",
- "FontVariantLigatures",
- engines="gecko",
- initial_value="computed::FontVariantLigatures::empty()",
- initial_specified_value="specified::FontVariantLigatures::empty()",
- animation_value_type="discrete",
- spec="https://drafts.csswg.org/css-fonts/#propdef-font-variant-ligatures",
-)}
-
-${helpers.predefined_type(
- "font-variant-numeric",
- "FontVariantNumeric",
- engines="gecko",
- initial_value="computed::FontVariantNumeric::empty()",
- initial_specified_value="specified::FontVariantNumeric::empty()",
- animation_value_type="discrete",
- spec="https://drafts.csswg.org/css-fonts/#propdef-font-variant-numeric",
-)}
-
-${helpers.single_keyword(
- "font-variant-position",
- "normal sub super",
- engines="gecko",
- gecko_ffi_name="mFont.variantPosition",
- gecko_constant_prefix="NS_FONT_VARIANT_POSITION",
- spec="https://drafts.csswg.org/css-fonts/#propdef-font-variant-position",
- animation_value_type="discrete",
-)}
-
-${helpers.predefined_type(
- "font-feature-settings",
- "FontFeatureSettings",
- engines="gecko",
- initial_value="computed::FontFeatureSettings::normal()",
- initial_specified_value="specified::FontFeatureSettings::normal()",
- extra_prefixes="moz:layout.css.prefixes.font-features",
- animation_value_type="discrete",
- spec="https://drafts.csswg.org/css-fonts/#propdef-font-feature-settings",
-)}
-
-${helpers.predefined_type(
- "font-variation-settings",
- "FontVariationSettings",
- engines="gecko",
- gecko_pref="layout.css.font-variations.enabled",
- has_effect_on_gecko_scrollbars=False,
- initial_value="computed::FontVariationSettings::normal()",
- initial_specified_value="specified::FontVariationSettings::normal()",
- animation_value_type="ComputedValue",
- spec="https://drafts.csswg.org/css-fonts-4/#propdef-font-variation-settings"
-)}
-
-${helpers.predefined_type(
- "font-language-override",
- "FontLanguageOverride",
- engines="gecko",
- initial_value="computed::FontLanguageOverride::normal()",
- initial_specified_value="specified::FontLanguageOverride::normal()",
- animation_value_type="discrete",
- extra_prefixes="moz:layout.css.prefixes.font-features",
- spec="https://drafts.csswg.org/css-fonts-3/#propdef-font-language-override",
-)}
-
-${helpers.single_keyword(
- "font-optical-sizing",
- "auto none",
- engines="gecko",
- gecko_pref="layout.css.font-variations.enabled",
- has_effect_on_gecko_scrollbars=False,
- gecko_ffi_name="mFont.opticalSizing",
- gecko_constant_prefix="NS_FONT_OPTICAL_SIZING",
- animation_value_type="discrete",
- spec="https://www.w3.org/TR/css-fonts-4/#font-optical-sizing-def",
-)}
-
-${helpers.predefined_type(
- "font-palette",
- "FontPalette",
- engines="gecko",
- initial_value="computed::FontPalette::normal()",
- initial_specified_value="specified::FontPalette::normal()",
- animation_value_type="discrete",
- gecko_pref="layout.css.font-palette.enabled",
- has_effect_on_gecko_scrollbars=False,
- spec="https://drafts.csswg.org/css-fonts/#font-palette-prop",
-)}
-
-${helpers.predefined_type(
- "-x-lang",
- "XLang",
- engines="gecko",
- initial_value="computed::XLang::get_initial_value()",
- animation_value_type="none",
- enabled_in="",
- has_effect_on_gecko_scrollbars=False,
- spec="Internal (not web-exposed)",
-)}
-
-${helpers.predefined_type(
- "-moz-script-size-multiplier",
- "MozScriptSizeMultiplier",
- engines="gecko",
- initial_value="computed::MozScriptSizeMultiplier::get_initial_value()",
- animation_value_type="none",
- gecko_ffi_name="mScriptSizeMultiplier",
- enabled_in="",
- has_effect_on_gecko_scrollbars=False,
- spec="Internal (not web-exposed)",
-)}
-
-${helpers.predefined_type(
- "math-depth",
- "MathDepth",
- "0",
- engines="gecko",
- gecko_pref="layout.css.math-depth.enabled",
- has_effect_on_gecko_scrollbars=False,
- animation_value_type="none",
- enabled_in="ua",
- spec="https://mathml-refresh.github.io/mathml-core/#the-math-script-level-property",
-)}
-
-${helpers.single_keyword(
- "math-style",
- "normal compact",
- engines="gecko",
- gecko_enum_prefix="StyleMathStyle",
- gecko_pref="layout.css.math-style.enabled",
- spec="https://mathml-refresh.github.io/mathml-core/#the-math-style-property",
- has_effect_on_gecko_scrollbars=False,
- animation_value_type="none",
- enabled_in="ua",
- needs_conversion=True,
-)}
-
-${helpers.single_keyword(
- "-moz-math-variant",
- """none normal bold italic bold-italic script bold-script
- fraktur double-struck bold-fraktur sans-serif
- bold-sans-serif sans-serif-italic sans-serif-bold-italic
- monospace initial tailed looped stretched""",
- engines="gecko",
- gecko_enum_prefix="StyleMathVariant",
- gecko_ffi_name="mMathVariant",
- spec="Internal (not web-exposed)",
- animation_value_type="none",
- enabled_in="",
- has_effect_on_gecko_scrollbars=False,
- needs_conversion=True,
-)}
-
-${helpers.predefined_type(
- "-moz-script-min-size",
- "MozScriptMinSize",
- "specified::MozScriptMinSize::get_initial_value()",
- engines="gecko",
- animation_value_type="none",
- enabled_in="",
- has_effect_on_gecko_scrollbars=False,
- gecko_ffi_name="mScriptMinSize",
- spec="Internal (not web-exposed)",
-)}
-
-${helpers.predefined_type(
- "-x-text-scale",
- "XTextScale",
- "computed::XTextScale::All",
- engines="gecko",
- animation_value_type="none",
- enabled_in="",
- has_effect_on_gecko_scrollbars=False,
- spec="Internal (not web-exposed)",
-)}
-
-% if engine == "gecko":
-pub mod system_font {
- //! We deal with system fonts here
- //!
- //! System fonts can only be set as a group via the font shorthand.
- //! They resolve at compute time (not parse time -- this lets the
- //! browser respond to changes to the OS font settings).
- //!
- //! While Gecko handles these as a separate property and keyword
- //! values on each property indicating that the font should be picked
- //! from the -x-system-font property, we avoid this. Instead,
- //! each font longhand has a special SystemFont variant which contains
- //! the specified system font. When the cascade function (in helpers)
- //! detects that a value has a system font, it will resolve it, and
- //! cache it on the ComputedValues. After this, it can be just fetched
- //! whenever a font longhand on the same element needs the system font.
- //!
- //! When a longhand property is holding a SystemFont, it's serialized
- //! to an empty string as if its value comes from a shorthand with
- //! variable reference. We may want to improve this behavior at some
- //! point. See also https://github.com/w3c/csswg-drafts/issues/1586.
-
- use crate::properties::longhands;
- use std::hash::{Hash, Hasher};
- use crate::values::computed::{ToComputedValue, Context};
- use crate::values::specified::font::SystemFont;
- // ComputedValues are compared at times
- // so we need these impls. We don't want to
- // add Eq to Number (which contains a float)
- // so instead we have an eq impl which skips the
- // cached values
- impl PartialEq for ComputedSystemFont {
- fn eq(&self, other: &Self) -> bool {
- self.system_font == other.system_font
- }
- }
- impl Eq for ComputedSystemFont {}
-
- impl Hash for ComputedSystemFont {
- fn hash<H: Hasher>(&self, hasher: &mut H) {
- self.system_font.hash(hasher)
- }
- }
-
- impl ToComputedValue for SystemFont {
- type ComputedValue = ComputedSystemFont;
-
- fn to_computed_value(&self, cx: &Context) -> Self::ComputedValue {
- use crate::gecko_bindings::bindings;
- use crate::gecko_bindings::structs::nsFont;
- use crate::values::computed::font::FontSize;
- use crate::values::specified::font::KeywordInfo;
- use crate::values::generics::NonNegative;
- use std::mem;
-
- let mut system = mem::MaybeUninit::<nsFont>::uninit();
- let system = unsafe {
- bindings::Gecko_nsFont_InitSystem(
- system.as_mut_ptr(),
- *self,
- &**cx.style().get_font(),
- cx.device().document()
- );
- &mut *system.as_mut_ptr()
- };
- let size = NonNegative(cx.maybe_zoom_text(system.size.0));
- let ret = ComputedSystemFont {
- font_family: system.family.clone(),
- font_size: FontSize {
- computed_size: size,
- used_size: size,
- keyword_info: KeywordInfo::none()
- },
- font_weight: system.weight,
- font_stretch: system.stretch,
- font_style: system.style,
- system_font: *self,
- };
- unsafe { bindings::Gecko_nsFont_Destroy(system); }
- ret
- }
-
- fn from_computed_value(_: &ComputedSystemFont) -> Self {
- unreachable!()
- }
- }
-
- #[inline]
- /// Compute and cache a system font
- ///
- /// Must be called before attempting to compute a system font
- /// specified value
- pub fn resolve_system_font(system: SystemFont, context: &mut Context) {
- // Checking if context.cached_system_font.is_none() isn't enough,
- // if animating from one system font to another the cached system font
- // may change
- if Some(system) != context.cached_system_font.as_ref().map(|x| x.system_font) {
- let computed = system.to_computed_value(context);
- context.cached_system_font = Some(computed);
- }
- }
-
- #[derive(Clone, Debug)]
- pub struct ComputedSystemFont {
- % for name in SYSTEM_FONT_LONGHANDS:
- pub ${name}: longhands::${name}::computed_value::T,
- % endfor
- pub system_font: SystemFont,
- }
-
-}
-% endif
-
-${helpers.single_keyword(
- "-moz-osx-font-smoothing",
- "auto grayscale",
- engines="gecko",
- gecko_constant_prefix="NS_FONT_SMOOTHING",
- gecko_ffi_name="mFont.smoothing",
- gecko_pref="layout.css.osx-font-smoothing.enabled",
- has_effect_on_gecko_scrollbars=False,
- spec="Nonstandard (https://developer.mozilla.org/en-US/docs/Web/CSS/font-smooth)",
- animation_value_type="discrete",
-)}
-
-${helpers.predefined_type(
- "-moz-font-smoothing-background-color",
- "color::MozFontSmoothingBackgroundColor",
- "computed::color::MozFontSmoothingBackgroundColor::transparent()",
- engines="gecko",
- animation_value_type="none",
- gecko_ffi_name="mFont.fontSmoothingBackgroundColor",
- enabled_in="chrome",
- spec="None (Nonstandard internal property)",
-)}
-
-${helpers.predefined_type(
- "-moz-min-font-size-ratio",
- "Percentage",
- "computed::Percentage::hundred()",
- engines="gecko",
- animation_value_type="none",
- enabled_in="ua",
- spec="Nonstandard (Internal-only)",
-)}
diff --git a/components/style/properties/longhands/inherited_box.mako.rs b/components/style/properties/longhands/inherited_box.mako.rs
deleted file mode 100644
index 947c66e9eed..00000000000
--- a/components/style/properties/longhands/inherited_box.mako.rs
+++ /dev/null
@@ -1,96 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
-
-<%namespace name="helpers" file="/helpers.mako.rs" />
-
-<% data.new_style_struct("InheritedBox", inherited=True, gecko_name="Visibility") %>
-
-// TODO: collapse. Well, do tables first.
-${helpers.single_keyword(
- "visibility",
- "visible hidden collapse",
- engines="gecko servo",
- gecko_ffi_name="mVisible",
- animation_value_type="ComputedValue",
- spec="https://drafts.csswg.org/css-box/#propdef-visibility",
- gecko_enum_prefix="StyleVisibility",
-)}
-
-// CSS Writing Modes Level 3
-// https://drafts.csswg.org/css-writing-modes-3
-${helpers.single_keyword(
- "writing-mode",
- "horizontal-tb vertical-rl vertical-lr",
- engines="gecko servo",
- extra_gecko_values="sideways-rl sideways-lr",
- gecko_aliases="lr=horizontal-tb lr-tb=horizontal-tb \
- rl=horizontal-tb rl-tb=horizontal-tb \
- tb=vertical-rl tb-rl=vertical-rl",
- servo_pref="layout.writing-mode.enabled",
- animation_value_type="none",
- spec="https://drafts.csswg.org/css-writing-modes/#propdef-writing-mode",
- gecko_enum_prefix="StyleWritingModeProperty",
- servo_restyle_damage="rebuild_and_reflow",
-)}
-
-${helpers.single_keyword(
- "direction",
- "ltr rtl",
- engines="gecko servo",
- servo_pref="layout.legacy_layout",
- animation_value_type="none",
- spec="https://drafts.csswg.org/css-writing-modes/#propdef-direction",
- gecko_enum_prefix="StyleDirection",
- servo_restyle_damage="rebuild_and_reflow",
-)}
-
-${helpers.single_keyword(
- "-moz-box-collapse",
- "flex legacy",
- engines="gecko",
- gecko_enum_prefix="StyleMozBoxCollapse",
- animation_value_type="none",
- enabled_in="chrome",
- spec="None (internal)",
-)}
-
-${helpers.single_keyword(
- "text-orientation",
- "mixed upright sideways",
- engines="gecko",
- gecko_aliases="sideways-right=sideways",
- gecko_enum_prefix="StyleTextOrientation",
- animation_value_type="none",
- spec="https://drafts.csswg.org/css-writing-modes/#propdef-text-orientation",
-)}
-
-${helpers.predefined_type(
- "print-color-adjust",
- "PrintColorAdjust",
- "computed::PrintColorAdjust::Economy",
- engines="gecko",
- aliases="color-adjust",
- spec="https://drafts.csswg.org/css-color-adjust/#print-color-adjust",
- animation_value_type="discrete",
-)}
-
-// According to to CSS-IMAGES-3, `optimizespeed` and `optimizequality` are synonyms for `auto`
-// And, firefox doesn't support `pixelated` yet (https://bugzilla.mozilla.org/show_bug.cgi?id=856337)
-${helpers.predefined_type(
- "image-rendering",
- "ImageRendering",
- "computed::ImageRendering::Auto",
- engines="gecko servo",
- spec="https://drafts.csswg.org/css-images/#propdef-image-rendering",
- animation_value_type="discrete",
-)}
-
-${helpers.single_keyword(
- "image-orientation",
- "from-image none",
- engines="gecko",
- gecko_enum_prefix="StyleImageOrientation",
- animation_value_type="discrete",
- spec="https://drafts.csswg.org/css-images/#propdef-image-orientation",
-)}
diff --git a/components/style/properties/longhands/inherited_svg.mako.rs b/components/style/properties/longhands/inherited_svg.mako.rs
deleted file mode 100644
index b9d55c01b20..00000000000
--- a/components/style/properties/longhands/inherited_svg.mako.rs
+++ /dev/null
@@ -1,217 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
-
-<%namespace name="helpers" file="/helpers.mako.rs" />
-
-// SVG 1.1 (Second Edition)
-// https://www.w3.org/TR/SVG/
-<% data.new_style_struct("InheritedSVG", inherited=True, gecko_name="SVG") %>
-
-// Section 10 - Text
-
-${helpers.single_keyword(
- "dominant-baseline",
- """auto ideographic alphabetic hanging mathematical central middle
- text-after-edge text-before-edge""",
- engines="gecko",
- animation_value_type="discrete",
- spec="https://www.w3.org/TR/css-inline-3/#propdef-dominant-baseline",
- gecko_enum_prefix="StyleDominantBaseline",
-)}
-
-${helpers.single_keyword(
- "text-anchor",
- "start middle end",
- engines="gecko",
- animation_value_type="discrete",
- spec="https://www.w3.org/TR/SVG/text.html#TextAnchorProperty",
- gecko_enum_prefix="StyleTextAnchor",
-)}
-
-// Section 11 - Painting: Filling, Stroking and Marker Symbols
-${helpers.single_keyword(
- "color-interpolation",
- "srgb auto linearrgb",
- engines="gecko",
- animation_value_type="discrete",
- spec="https://www.w3.org/TR/SVG11/painting.html#ColorInterpolationProperty",
- gecko_enum_prefix="StyleColorInterpolation",
-)}
-
-${helpers.single_keyword(
- "color-interpolation-filters",
- "linearrgb auto srgb",
- engines="gecko",
- animation_value_type="discrete",
- spec="https://www.w3.org/TR/SVG11/painting.html#ColorInterpolationFiltersProperty",
- gecko_enum_prefix="StyleColorInterpolation",
-)}
-
-${helpers.predefined_type(
- "fill",
- "SVGPaint",
- "crate::values::computed::SVGPaint::black()",
- engines="gecko",
- animation_value_type="IntermediateSVGPaint",
- boxed=True,
- spec="https://www.w3.org/TR/SVG2/painting.html#SpecifyingFillPaint",
-)}
-
-${helpers.predefined_type(
- "fill-opacity",
- "SVGOpacity",
- "Default::default()",
- engines="gecko",
- animation_value_type="ComputedValue",
- spec="https://svgwg.org/svg2-draft/painting.html#FillOpacity",
-)}
-
-${helpers.predefined_type(
- "fill-rule",
- "FillRule",
- "Default::default()",
- engines="gecko",
- animation_value_type="discrete",
- spec="https://www.w3.org/TR/SVG11/painting.html#FillRuleProperty",
-)}
-
-${helpers.single_keyword(
- "shape-rendering",
- "auto optimizespeed crispedges geometricprecision",
- engines="gecko",
- animation_value_type="discrete",
- spec="https://www.w3.org/TR/SVG11/painting.html#ShapeRenderingProperty",
- gecko_enum_prefix = "StyleShapeRendering",
-)}
-
-${helpers.predefined_type(
- "stroke",
- "SVGPaint",
- "Default::default()",
- engines="gecko",
- animation_value_type="IntermediateSVGPaint",
- boxed=True,
- spec="https://www.w3.org/TR/SVG2/painting.html#SpecifyingStrokePaint",
-)}
-
-${helpers.predefined_type(
- "stroke-width",
- "SVGWidth",
- "computed::SVGWidth::one()",
- engines="gecko",
- animation_value_type="crate::values::computed::SVGWidth",
- spec="https://www.w3.org/TR/SVG2/painting.html#StrokeWidth",
-)}
-
-${helpers.single_keyword(
- "stroke-linecap",
- "butt round square",
- engines="gecko",
- animation_value_type="discrete",
- spec="https://www.w3.org/TR/SVG11/painting.html#StrokeLinecapProperty",
- gecko_enum_prefix = "StyleStrokeLinecap",
-)}
-
-${helpers.single_keyword(
- "stroke-linejoin",
- "miter round bevel",
- engines="gecko",
- animation_value_type="discrete",
- spec="https://www.w3.org/TR/SVG11/painting.html#StrokeLinejoinProperty",
- gecko_enum_prefix = "StyleStrokeLinejoin",
-)}
-
-${helpers.predefined_type(
- "stroke-miterlimit",
- "NonNegativeNumber",
- "From::from(4.0)",
- engines="gecko",
- animation_value_type="crate::values::computed::NonNegativeNumber",
- spec="https://www.w3.org/TR/SVG2/painting.html#StrokeMiterlimitProperty",
-)}
-
-${helpers.predefined_type(
- "stroke-opacity",
- "SVGOpacity",
- "Default::default()",
- engines="gecko",
- animation_value_type="ComputedValue",
- spec="https://svgwg.org/svg2-draft/painting.html#StrokeOpacity",
-)}
-
-${helpers.predefined_type(
- "stroke-dasharray",
- "SVGStrokeDashArray",
- "Default::default()",
- engines="gecko",
- animation_value_type="crate::values::computed::SVGStrokeDashArray",
- spec="https://www.w3.org/TR/SVG2/painting.html#StrokeDashing",
-)}
-
-${helpers.predefined_type(
- "stroke-dashoffset",
- "SVGLength",
- "computed::SVGLength::zero()",
- engines="gecko",
- animation_value_type="ComputedValue",
- spec="https://www.w3.org/TR/SVG2/painting.html#StrokeDashing",
-)}
-
-// Section 14 - Clipping, Masking and Compositing
-${helpers.predefined_type(
- "clip-rule",
- "FillRule",
- "Default::default()",
- engines="gecko",
- animation_value_type="discrete",
- spec="https://www.w3.org/TR/SVG11/masking.html#ClipRuleProperty",
-)}
-
-${helpers.predefined_type(
- "marker-start",
- "url::UrlOrNone",
- "computed::url::UrlOrNone::none()",
- engines="gecko",
- animation_value_type="discrete",
- spec="https://www.w3.org/TR/SVG2/painting.html#VertexMarkerProperties",
-)}
-
-${helpers.predefined_type(
- "marker-mid",
- "url::UrlOrNone",
- "computed::url::UrlOrNone::none()",
- engines="gecko",
- animation_value_type="discrete",
- spec="https://www.w3.org/TR/SVG2/painting.html#VertexMarkerProperties",
-)}
-
-${helpers.predefined_type(
- "marker-end",
- "url::UrlOrNone",
- "computed::url::UrlOrNone::none()",
- engines="gecko",
- animation_value_type="discrete",
- spec="https://www.w3.org/TR/SVG2/painting.html#VertexMarkerProperties",
-)}
-
-${helpers.predefined_type(
- "paint-order",
- "SVGPaintOrder",
- "computed::SVGPaintOrder::normal()",
- engines="gecko",
- animation_value_type="discrete",
- spec="https://www.w3.org/TR/SVG2/painting.html#PaintOrder",
-)}
-
-${helpers.predefined_type(
- "-moz-context-properties",
- "MozContextProperties",
- "computed::MozContextProperties::default()",
- engines="gecko",
- enabled_in="chrome",
- gecko_pref="svg.context-properties.content.enabled",
- has_effect_on_gecko_scrollbars=False,
- animation_value_type="none",
- spec="Nonstandard (https://developer.mozilla.org/en-US/docs/Web/CSS/-moz-context-properties)",
-)}
diff --git a/components/style/properties/longhands/inherited_table.mako.rs b/components/style/properties/longhands/inherited_table.mako.rs
deleted file mode 100644
index a720c33f47e..00000000000
--- a/components/style/properties/longhands/inherited_table.mako.rs
+++ /dev/null
@@ -1,51 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
-
-<%namespace name="helpers" file="/helpers.mako.rs" />
-
-<% data.new_style_struct("InheritedTable", inherited=True, gecko_name="TableBorder") %>
-
-${helpers.single_keyword(
- "border-collapse",
- "separate collapse",
- engines="gecko servo",
- servo_pref="layout.legacy_layout",
- gecko_enum_prefix="StyleBorderCollapse",
- animation_value_type="discrete",
- spec="https://drafts.csswg.org/css-tables/#propdef-border-collapse",
- servo_restyle_damage = "reflow",
-)}
-
-${helpers.single_keyword(
- "empty-cells",
- "show hide",
- engines="gecko servo",
- servo_pref="layout.legacy_layout",
- gecko_enum_prefix="StyleEmptyCells",
- animation_value_type="discrete",
- spec="https://drafts.csswg.org/css-tables/#propdef-empty-cells",
- servo_restyle_damage="rebuild_and_reflow",
-)}
-
-${helpers.predefined_type(
- "caption-side",
- "table::CaptionSide",
- "computed::table::CaptionSide::Top",
- engines="gecko servo",
- servo_pref="layout.legacy_layout",
- animation_value_type="discrete",
- spec="https://drafts.csswg.org/css-tables/#propdef-caption-side",
- servo_restyle_damage="rebuild_and_reflow",
-)}
-
-${helpers.predefined_type(
- "border-spacing",
- "BorderSpacing",
- "computed::BorderSpacing::zero()",
- engines="gecko servo",
- animation_value_type="BorderSpacing",
- boxed=True,
- spec="https://drafts.csswg.org/css-tables/#propdef-border-spacing",
- servo_restyle_damage="reflow",
-)}
diff --git a/components/style/properties/longhands/inherited_text.mako.rs b/components/style/properties/longhands/inherited_text.mako.rs
deleted file mode 100644
index 455812a8df0..00000000000
--- a/components/style/properties/longhands/inherited_text.mako.rs
+++ /dev/null
@@ -1,407 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
-
-<%namespace name="helpers" file="/helpers.mako.rs" />
-<% from data import Keyword %>
-<% data.new_style_struct("InheritedText", inherited=True, gecko_name="Text") %>
-
-${helpers.predefined_type(
- "color",
- "ColorPropertyValue",
- "crate::color::AbsoluteColor::black()",
- engines="gecko servo",
- animation_value_type="AbsoluteColor",
- ignored_when_colors_disabled="True",
- spec="https://drafts.csswg.org/css-color/#color",
-)}
-
-${helpers.predefined_type(
- "line-height",
- "LineHeight",
- "computed::LineHeight::normal()",
- engines="gecko servo",
- animation_value_type="LineHeight",
- spec="https://drafts.csswg.org/css2/visudet.html#propdef-line-height",
- servo_restyle_damage="reflow"
-)}
-
-// CSS Text Module Level 3
-
-${helpers.predefined_type(
- "text-transform",
- "TextTransform",
- "computed::TextTransform::none()",
- engines="gecko servo",
- animation_value_type="discrete",
- spec="https://drafts.csswg.org/css-text/#propdef-text-transform",
- servo_restyle_damage="rebuild_and_reflow",
-)}
-
-${helpers.single_keyword(
- "hyphens",
- "manual none auto",
- engines="gecko",
- gecko_enum_prefix="StyleHyphens",
- animation_value_type="discrete",
- extra_prefixes="moz",
- spec="https://drafts.csswg.org/css-text/#propdef-hyphens",
-)}
-
-// TODO: Support <percentage>
-${helpers.single_keyword(
- "-moz-text-size-adjust",
- "auto none",
- engines="gecko",
- gecko_enum_prefix="StyleTextSizeAdjust",
- gecko_ffi_name="mTextSizeAdjust",
- animation_value_type="discrete",
- spec="https://drafts.csswg.org/css-size-adjust/#adjustment-control",
- aliases="-webkit-text-size-adjust",
-)}
-
-${helpers.predefined_type(
- "text-indent",
- "LengthPercentage",
- "computed::LengthPercentage::zero()",
- engines="gecko servo",
- animation_value_type="ComputedValue",
- spec="https://drafts.csswg.org/css-text/#propdef-text-indent",
- allow_quirks="Yes",
- servo_restyle_damage = "reflow",
-)}
-
-// Also known as "word-wrap" (which is more popular because of IE), but this is
-// the preferred name per CSS-TEXT 6.2.
-${helpers.predefined_type(
- "overflow-wrap",
- "OverflowWrap",
- "computed::OverflowWrap::Normal",
- engines="gecko servo",
- servo_pref="layout.legacy_layout",
- animation_value_type="discrete",
- spec="https://drafts.csswg.org/css-text/#propdef-overflow-wrap",
- aliases="word-wrap",
- servo_restyle_damage="rebuild_and_reflow",
-)}
-
-${helpers.predefined_type(
- "word-break",
- "WordBreak",
- "computed::WordBreak::Normal",
- engines="gecko servo",
- servo_pref="layout.legacy_layout",
- animation_value_type="discrete",
- spec="https://drafts.csswg.org/css-text/#propdef-word-break",
- servo_restyle_damage="rebuild_and_reflow",
-)}
-
-${helpers.predefined_type(
- "text-justify",
- "TextJustify",
- "computed::TextJustify::Auto",
- engines="gecko servo",
- animation_value_type="discrete",
- spec="https://drafts.csswg.org/css-text/#propdef-text-justify",
- servo_restyle_damage="rebuild_and_reflow",
-)}
-
-${helpers.predefined_type(
- "text-align-last",
- "TextAlignLast",
- "computed::text::TextAlignLast::Auto",
- engines="gecko servo",
- animation_value_type="discrete",
- spec="https://drafts.csswg.org/css-text/#propdef-text-align-last",
-)}
-
-// TODO make this a shorthand and implement text-align-last/text-align-all
-${helpers.predefined_type(
- "text-align",
- "TextAlign",
- "computed::TextAlign::Start",
- engines="gecko servo",
- animation_value_type="discrete",
- spec="https://drafts.csswg.org/css-text/#propdef-text-align",
- servo_restyle_damage = "reflow",
-)}
-
-${helpers.predefined_type(
- "letter-spacing",
- "LetterSpacing",
- "computed::LetterSpacing::normal()",
- engines="gecko servo",
- animation_value_type="ComputedValue",
- spec="https://drafts.csswg.org/css-text/#propdef-letter-spacing",
- servo_restyle_damage="rebuild_and_reflow",
-)}
-
-${helpers.predefined_type(
- "word-spacing",
- "WordSpacing",
- "computed::WordSpacing::zero()",
- engines="gecko servo",
- animation_value_type="ComputedValue",
- spec="https://drafts.csswg.org/css-text/#propdef-word-spacing",
- servo_restyle_damage="rebuild_and_reflow",
-)}
-
-<%helpers:single_keyword
- name="white-space"
- values="normal pre nowrap pre-wrap pre-line"
- engines="gecko servo",
- extra_gecko_values="break-spaces -moz-pre-space"
- gecko_enum_prefix="StyleWhiteSpace"
- needs_conversion="True"
- animation_value_type="discrete"
- spec="https://drafts.csswg.org/css-text/#propdef-white-space"
- servo_restyle_damage="rebuild_and_reflow"
->
- % if engine == "servo":
- impl SpecifiedValue {
- pub fn allow_wrap(&self) -> bool {
- match *self {
- SpecifiedValue::Nowrap |
- SpecifiedValue::Pre => false,
- SpecifiedValue::Normal |
- SpecifiedValue::PreWrap |
- SpecifiedValue::PreLine => true,
- }
- }
-
- pub fn preserve_newlines(&self) -> bool {
- match *self {
- SpecifiedValue::Normal |
- SpecifiedValue::Nowrap => false,
- SpecifiedValue::Pre |
- SpecifiedValue::PreWrap |
- SpecifiedValue::PreLine => true,
- }
- }
-
- pub fn preserve_spaces(&self) -> bool {
- match *self {
- SpecifiedValue::Normal |
- SpecifiedValue::Nowrap |
- SpecifiedValue::PreLine => false,
- SpecifiedValue::Pre |
- SpecifiedValue::PreWrap => true,
- }
- }
- }
- % endif
-</%helpers:single_keyword>
-
-${helpers.predefined_type(
- "text-shadow",
- "SimpleShadow",
- None,
- engines="gecko servo",
- servo_pref="layout.legacy_layout",
- vector=True,
- vector_animation_type="with_zero",
- animation_value_type="AnimatedTextShadowList",
- ignored_when_colors_disabled=True,
- simple_vector_bindings=True,
- spec="https://drafts.csswg.org/css-text-decor-3/#text-shadow-property",
-)}
-
-${helpers.predefined_type(
- "text-emphasis-style",
- "TextEmphasisStyle",
- "computed::TextEmphasisStyle::None",
- engines="gecko",
- initial_specified_value="SpecifiedValue::None",
- animation_value_type="discrete",
- spec="https://drafts.csswg.org/css-text-decor/#propdef-text-emphasis-style",
-)}
-
-${helpers.predefined_type(
- "text-emphasis-position",
- "TextEmphasisPosition",
- "computed::TextEmphasisPosition::OVER",
- engines="gecko",
- initial_specified_value="specified::TextEmphasisPosition::OVER",
- animation_value_type="discrete",
- spec="https://drafts.csswg.org/css-text-decor/#propdef-text-emphasis-position",
-)}
-
-${helpers.predefined_type(
- "text-emphasis-color",
- "Color",
- "computed_value::T::currentcolor()",
- engines="gecko",
- initial_specified_value="specified::Color::currentcolor()",
- animation_value_type="AnimatedColor",
- ignored_when_colors_disabled=True,
- spec="https://drafts.csswg.org/css-text-decor/#propdef-text-emphasis-color",
-)}
-
-${helpers.predefined_type(
- "tab-size",
- "NonNegativeLengthOrNumber",
- "generics::length::LengthOrNumber::Number(From::from(8.0))",
- engines="gecko",
- animation_value_type="LengthOrNumber",
- spec="https://drafts.csswg.org/css-text-3/#tab-size-property",
- aliases="-moz-tab-size",
-)}
-
-${helpers.predefined_type(
- "line-break",
- "LineBreak",
- "computed::LineBreak::Auto",
- engines="gecko",
- animation_value_type="discrete",
- spec="https://drafts.csswg.org/css-text-3/#line-break-property",
-)}
-
-// CSS Compatibility
-// https://compat.spec.whatwg.org
-${helpers.predefined_type(
- "-webkit-text-fill-color",
- "Color",
- "computed_value::T::currentcolor()",
- engines="gecko",
- animation_value_type="AnimatedColor",
- ignored_when_colors_disabled=True,
- spec="https://compat.spec.whatwg.org/#the-webkit-text-fill-color",
-)}
-
-${helpers.predefined_type(
- "-webkit-text-stroke-color",
- "Color",
- "computed_value::T::currentcolor()",
- initial_specified_value="specified::Color::currentcolor()",
- engines="gecko",
- animation_value_type="AnimatedColor",
- ignored_when_colors_disabled=True,
- spec="https://compat.spec.whatwg.org/#the-webkit-text-stroke-color",
-)}
-
-${helpers.predefined_type(
- "-webkit-text-stroke-width",
- "LineWidth",
- "app_units::Au(0)",
- engines="gecko",
- initial_specified_value="specified::LineWidth::zero()",
- spec="https://compat.spec.whatwg.org/#the-webkit-text-stroke-width",
- animation_value_type="discrete",
-)}
-
-// CSS Ruby Layout Module Level 1
-// https://drafts.csswg.org/css-ruby/
-${helpers.single_keyword(
- "ruby-align",
- "space-around start center space-between",
- engines="gecko",
- animation_value_type="discrete",
- gecko_enum_prefix="StyleRubyAlign",
- spec="https://drafts.csswg.org/css-ruby/#ruby-align-property",
-)}
-
-${helpers.predefined_type(
- "ruby-position",
- "RubyPosition",
- "computed::RubyPosition::AlternateOver",
- engines="gecko",
- spec="https://drafts.csswg.org/css-ruby/#ruby-position-property",
- animation_value_type="discrete",
-)}
-
-// CSS Writing Modes Module Level 3
-// https://drafts.csswg.org/css-writing-modes-3/
-
-${helpers.single_keyword(
- "text-combine-upright",
- "none all",
- engines="gecko",
- gecko_enum_prefix="StyleTextCombineUpright",
- animation_value_type="none",
- spec="https://drafts.csswg.org/css-writing-modes-3/#text-combine-upright",
-)}
-
-// SVG 1.1: Section 11 - Painting: Filling, Stroking and Marker Symbols
-${helpers.single_keyword(
- "text-rendering",
- "auto optimizespeed optimizelegibility geometricprecision",
- engines="gecko servo",
- gecko_enum_prefix="StyleTextRendering",
- animation_value_type="discrete",
- spec="https://www.w3.org/TR/SVG11/painting.html#TextRenderingProperty",
- servo_restyle_damage="rebuild_and_reflow",
-)}
-
-${helpers.predefined_type(
- "-moz-control-character-visibility",
- "text::MozControlCharacterVisibility",
- "Default::default()",
- engines="gecko",
- enabled_in="chrome",
- gecko_pref="layout.css.moz-control-character-visibility.enabled",
- has_effect_on_gecko_scrollbars=False,
- animation_value_type="none",
- spec="Nonstandard"
-)}
-
-// text underline offset
-${helpers.predefined_type(
- "text-underline-offset",
- "LengthPercentageOrAuto",
- "computed::LengthPercentageOrAuto::auto()",
- engines="gecko",
- animation_value_type="ComputedValue",
- spec="https://drafts.csswg.org/css-text-decor-4/#underline-offset",
-)}
-
-// text underline position
-${helpers.predefined_type(
- "text-underline-position",
- "TextUnderlinePosition",
- "computed::TextUnderlinePosition::AUTO",
- engines="gecko",
- animation_value_type="discrete",
- spec="https://drafts.csswg.org/css-text-decor-3/#text-underline-position-property",
-)}
-
-// text decoration skip ink
-${helpers.predefined_type(
- "text-decoration-skip-ink",
- "TextDecorationSkipInk",
- "computed::TextDecorationSkipInk::Auto",
- engines="gecko",
- animation_value_type="discrete",
- spec="https://drafts.csswg.org/css-text-decor-4/#text-decoration-skip-ink-property",
-)}
-
-// hyphenation character
-${helpers.predefined_type(
- "hyphenate-character",
- "HyphenateCharacter",
- "computed::HyphenateCharacter::Auto",
- engines="gecko",
- gecko_pref="layout.css.hyphenate-character.enabled",
- has_effect_on_gecko_scrollbars=False,
- animation_value_type="discrete",
- spec="https://www.w3.org/TR/css-text-4/#hyphenate-character",
-)}
-
-${helpers.predefined_type(
- "forced-color-adjust",
- "ForcedColorAdjust",
- "computed::ForcedColorAdjust::Auto",
- engines="gecko",
- gecko_pref="layout.css.forced-color-adjust.enabled",
- has_effect_on_gecko_scrollbars=False,
- animation_value_type="discrete",
- spec="https://drafts.csswg.org/css-color-adjust-1/#forced-color-adjust-prop",
-)}
-
-${helpers.single_keyword(
- "-webkit-text-security",
- "none circle disc square",
- engines="gecko",
- gecko_enum_prefix="StyleTextSecurity",
- animation_value_type="discrete",
- spec="https://drafts.csswg.org/css-text/#MISSING",
-)}
diff --git a/components/style/properties/longhands/inherited_ui.mako.rs b/components/style/properties/longhands/inherited_ui.mako.rs
deleted file mode 100644
index 9c1218118e0..00000000000
--- a/components/style/properties/longhands/inherited_ui.mako.rs
+++ /dev/null
@@ -1,118 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
-
-<%namespace name="helpers" file="/helpers.mako.rs" />
-
-<% data.new_style_struct("InheritedUI", inherited=True, gecko_name="UI") %>
-
-${helpers.predefined_type(
- "cursor",
- "Cursor",
- "computed::Cursor::auto()",
- engines="gecko servo",
- initial_specified_value="specified::Cursor::auto()",
- animation_value_type="discrete",
- spec="https://drafts.csswg.org/css-ui/#cursor",
-)}
-
-// NB: `pointer-events: auto` (and use of `pointer-events` in anything that isn't SVG, in fact)
-// is nonstandard, slated for CSS4-UI.
-// TODO(pcwalton): SVG-only values.
-${helpers.single_keyword(
- "pointer-events",
- "auto none",
- engines="gecko servo",
- animation_value_type="discrete",
- extra_gecko_values="visiblepainted visiblefill visiblestroke visible painted fill stroke all",
- spec="https://www.w3.org/TR/SVG11/interact.html#PointerEventsProperty",
- gecko_enum_prefix="StylePointerEvents",
-)}
-
-${helpers.single_keyword(
- "-moz-inert",
- "none inert",
- engines="gecko",
- gecko_ffi_name="mInert",
- gecko_enum_prefix="StyleInert",
- animation_value_type="discrete",
- enabled_in="ua",
- spec="Nonstandard (https://html.spec.whatwg.org/multipage/#inert-subtrees)",
-)}
-
-${helpers.single_keyword(
- "-moz-user-input",
- "auto none",
- engines="gecko",
- gecko_ffi_name="mUserInput",
- gecko_enum_prefix="StyleUserInput",
- animation_value_type="discrete",
- spec="Nonstandard (https://developer.mozilla.org/en-US/docs/Web/CSS/-moz-user-input)",
-)}
-
-${helpers.single_keyword(
- "-moz-user-modify",
- "read-only read-write write-only",
- engines="gecko",
- gecko_ffi_name="mUserModify",
- gecko_enum_prefix="StyleUserModify",
- needs_conversion=True,
- animation_value_type="discrete",
- spec="Nonstandard (https://developer.mozilla.org/en-US/docs/Web/CSS/-moz-user-modify)",
-)}
-
-${helpers.single_keyword(
- "-moz-user-focus",
- "none ignore normal select-after select-before select-menu select-same select-all",
- engines="gecko",
- gecko_ffi_name="mUserFocus",
- gecko_enum_prefix="StyleUserFocus",
- animation_value_type="discrete",
- spec="Nonstandard (https://developer.mozilla.org/en-US/docs/Web/CSS/-moz-user-focus)",
-)}
-
-${helpers.predefined_type(
- "caret-color",
- "color::CaretColor",
- "generics::color::CaretColor::auto()",
- engines="gecko",
- spec="https://drafts.csswg.org/css-ui/#caret-color",
- animation_value_type="CaretColor",
- ignored_when_colors_disabled=True,
-)}
-
-${helpers.predefined_type(
- "accent-color",
- "ColorOrAuto",
- "generics::color::ColorOrAuto::Auto",
- engines="gecko",
- spec="https://drafts.csswg.org/css-ui-4/#widget-accent",
- gecko_pref="layout.css.accent-color.enabled",
- animation_value_type="ColorOrAuto",
- ignored_when_colors_disabled=True,
- has_effect_on_gecko_scrollbars=False,
-)}
-
-${helpers.predefined_type(
- "color-scheme",
- "ColorScheme",
- "specified::color::ColorScheme::normal()",
- engines="gecko",
- spec="https://drafts.csswg.org/css-color-adjust/#color-scheme-prop",
- gecko_pref="layout.css.color-scheme.enabled",
- animation_value_type="discrete",
- has_effect_on_gecko_scrollbars=False,
- ignored_when_colors_disabled=True,
- enabled_in="chrome",
-)}
-
-${helpers.predefined_type(
- "scrollbar-color",
- "ui::ScrollbarColor",
- "Default::default()",
- engines="gecko",
- spec="https://drafts.csswg.org/css-scrollbars-1/#scrollbar-color",
- animation_value_type="ScrollbarColor",
- boxed=True,
- ignored_when_colors_disabled=True,
-)}
diff --git a/components/style/properties/longhands/list.mako.rs b/components/style/properties/longhands/list.mako.rs
deleted file mode 100644
index f3c84295afc..00000000000
--- a/components/style/properties/longhands/list.mako.rs
+++ /dev/null
@@ -1,76 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
-
-<%namespace name="helpers" file="/helpers.mako.rs" />
-
-<% data.new_style_struct("List", inherited=True) %>
-
-${helpers.single_keyword(
- "list-style-position",
- "outside inside",
- engines="gecko servo",
- servo_pref="layout.legacy_layout",
- gecko_enum_prefix="StyleListStylePosition",
- animation_value_type="discrete",
- spec="https://drafts.csswg.org/css-lists/#propdef-list-style-position",
- servo_restyle_damage="rebuild_and_reflow",
-)}
-
-// TODO(pcwalton): Implement the full set of counter styles per CSS-COUNTER-STYLES [1] 6.1:
-//
-// decimal-leading-zero, armenian, upper-armenian, lower-armenian, georgian, lower-roman,
-// upper-roman
-//
-// [1]: http://dev.w3.org/csswg/css-counter-styles/
-% if engine == "servo":
- ${helpers.single_keyword(
- "list-style-type",
- """disc none circle square disclosure-open disclosure-closed
- decimal lower-alpha upper-alpha arabic-indic bengali cambodian cjk-decimal devanagari
- gujarati gurmukhi kannada khmer lao malayalam mongolian myanmar oriya persian telugu
- thai tibetan cjk-earthly-branch cjk-heavenly-stem lower-greek hiragana hiragana-iroha
- katakana katakana-iroha
- """,
- engines="servo",
- animation_value_type="discrete",
- spec="https://drafts.csswg.org/css-lists/#propdef-list-style-type",
- servo_restyle_damage="rebuild_and_reflow",
- )}
-% endif
-% if engine == "gecko":
- ${helpers.predefined_type(
- "list-style-type",
- "ListStyleType",
- "computed::ListStyleType::disc()",
- engines="gecko",
- initial_specified_value="specified::ListStyleType::disc()",
- animation_value_type="discrete",
- boxed=True,
- spec="https://drafts.csswg.org/css-lists/#propdef-list-style-type",
- servo_restyle_damage="rebuild_and_reflow",
- )}
-% endif
-
-${helpers.predefined_type(
- "list-style-image",
- "Image",
- engines="gecko servo",
- initial_value="computed::Image::None",
- initial_specified_value="specified::Image::None",
- animation_value_type="discrete",
- spec="https://drafts.csswg.org/css-lists/#propdef-list-style-image",
- boxed=engine == "servo",
- servo_restyle_damage="rebuild_and_reflow",
-)}
-
-${helpers.predefined_type(
- "quotes",
- "Quotes",
- "computed::Quotes::get_initial_value()",
- engines="gecko servo",
- servo_pref="layout.legacy_layout",
- animation_value_type="discrete",
- spec="https://drafts.csswg.org/css-content/#propdef-quotes",
- servo_restyle_damage="rebuild_and_reflow",
-)}
diff --git a/components/style/properties/longhands/margin.mako.rs b/components/style/properties/longhands/margin.mako.rs
deleted file mode 100644
index 251ee85f265..00000000000
--- a/components/style/properties/longhands/margin.mako.rs
+++ /dev/null
@@ -1,52 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
-
-<%namespace name="helpers" file="/helpers.mako.rs" />
-<% from data import ALL_SIDES, DEFAULT_RULES_AND_PAGE, maybe_moz_logical_alias %>
-<% data.new_style_struct("Margin", inherited=False) %>
-
-% for side in ALL_SIDES:
- <%
- spec = "https://drafts.csswg.org/css-box/#propdef-margin-%s" % side[0]
- if side[1]:
- spec = "https://drafts.csswg.org/css-logical-props/#propdef-margin-%s" % side[1]
- %>
- ${helpers.predefined_type(
- "margin-%s" % side[0],
- "LengthPercentageOrAuto",
- "computed::LengthPercentageOrAuto::zero()",
- engines="gecko servo",
- aliases=maybe_moz_logical_alias(engine, side, "-moz-margin-%s"),
- allow_quirks="No" if side[1] else "Yes",
- animation_value_type="ComputedValue",
- logical=side[1],
- logical_group="margin",
- spec=spec,
- rule_types_allowed=DEFAULT_RULES_AND_PAGE,
- servo_restyle_damage="reflow"
- )}
-% endfor
-
-${helpers.predefined_type(
- "overflow-clip-margin",
- "Length",
- "computed::Length::zero()",
- parse_method="parse_non_negative",
- engines="gecko",
- spec="https://drafts.csswg.org/css-overflow/#propdef-overflow-clip-margin",
- animation_value_type="ComputedValue",
-)}
-
-% for side in ALL_SIDES:
- ${helpers.predefined_type(
- "scroll-margin-%s" % side[0],
- "Length",
- "computed::Length::zero()",
- engines="gecko",
- logical=side[1],
- logical_group="scroll-margin",
- spec="https://drafts.csswg.org/css-scroll-snap-1/#propdef-scroll-margin-%s" % side[0],
- animation_value_type="ComputedValue",
- )}
-% endfor
diff --git a/components/style/properties/longhands/outline.mako.rs b/components/style/properties/longhands/outline.mako.rs
deleted file mode 100644
index 714cbc81ecc..00000000000
--- a/components/style/properties/longhands/outline.mako.rs
+++ /dev/null
@@ -1,51 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
-
-<%namespace name="helpers" file="/helpers.mako.rs" />
-<% from data import Method %>
-
-<% data.new_style_struct("Outline",
- inherited=False,
- additional_methods=[Method("outline_has_nonzero_width", "bool")]) %>
-
-// TODO(pcwalton): `invert`
-${helpers.predefined_type(
- "outline-color",
- "Color",
- "computed_value::T::currentcolor()",
- engines="gecko servo",
- initial_specified_value="specified::Color::currentcolor()",
- animation_value_type="AnimatedColor",
- ignored_when_colors_disabled=True,
- spec="https://drafts.csswg.org/css-ui/#propdef-outline-color",
-)}
-
-${helpers.predefined_type(
- "outline-style",
- "OutlineStyle",
- "computed::OutlineStyle::none()",
- engines="gecko servo",
- initial_specified_value="specified::OutlineStyle::none()",
- animation_value_type="discrete",
- spec="https://drafts.csswg.org/css-ui/#propdef-outline-style",
-)}
-
-${helpers.predefined_type(
- "outline-width",
- "BorderSideWidth",
- "app_units::Au::from_px(3)",
- engines="gecko servo",
- initial_specified_value="specified::BorderSideWidth::medium()",
- animation_value_type="NonNegativeLength",
- spec="https://drafts.csswg.org/css-ui/#propdef-outline-width",
-)}
-
-${helpers.predefined_type(
- "outline-offset",
- "Length",
- "crate::values::computed::Length::new(0.)",
- engines="gecko servo",
- animation_value_type="ComputedValue",
- spec="https://drafts.csswg.org/css-ui/#propdef-outline-offset",
-)}
diff --git a/components/style/properties/longhands/padding.mako.rs b/components/style/properties/longhands/padding.mako.rs
deleted file mode 100644
index 5b7b95cc3b3..00000000000
--- a/components/style/properties/longhands/padding.mako.rs
+++ /dev/null
@@ -1,41 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
-
-<%namespace name="helpers" file="/helpers.mako.rs" />
-<% from data import ALL_SIDES, maybe_moz_logical_alias %>
-<% data.new_style_struct("Padding", inherited=False) %>
-
-% for side in ALL_SIDES:
- <%
- spec = "https://drafts.csswg.org/css-box/#propdef-padding-%s" % side[0]
- if side[1]:
- spec = "https://drafts.csswg.org/css-logical-props/#propdef-padding-%s" % side[1]
- %>
- ${helpers.predefined_type(
- "padding-%s" % side[0],
- "NonNegativeLengthPercentage",
- "computed::NonNegativeLengthPercentage::zero()",
- engines="gecko servo",
- aliases=maybe_moz_logical_alias(engine, side, "-moz-padding-%s"),
- animation_value_type="NonNegativeLengthPercentage",
- logical=side[1],
- logical_group="padding",
- spec=spec,
- allow_quirks="No" if side[1] else "Yes",
- servo_restyle_damage="reflow rebuild_and_reflow_inline"
- )}
-% endfor
-
-% for side in ALL_SIDES:
- ${helpers.predefined_type(
- "scroll-padding-%s" % side[0],
- "NonNegativeLengthPercentageOrAuto",
- "computed::NonNegativeLengthPercentageOrAuto::auto()",
- engines="gecko",
- logical=side[1],
- logical_group="scroll-padding",
- spec="https://drafts.csswg.org/css-scroll-snap-1/#propdef-scroll-padding-%s" % side[0],
- animation_value_type="NonNegativeLengthPercentageOrAuto",
- )}
-% endfor
diff --git a/components/style/properties/longhands/page.mako.rs b/components/style/properties/longhands/page.mako.rs
deleted file mode 100644
index b857ce0ca39..00000000000
--- a/components/style/properties/longhands/page.mako.rs
+++ /dev/null
@@ -1,42 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
-
-<%namespace name="helpers" file="/helpers.mako.rs" />
-<% from data import PAGE_RULE %>
-
-<% data.new_style_struct("Page", inherited=False) %>
-
-${helpers.predefined_type(
- "size",
- "PageSize",
- "computed::PageSize::auto()",
- engines="gecko",
- gecko_pref="layout.css.page-size.enabled",
- initial_specified_value="specified::PageSize::auto()",
- spec="https://drafts.csswg.org/css-page-3/#page-size-prop",
- boxed=True,
- animation_value_type="none",
- rule_types_allowed=PAGE_RULE,
-)}
-
-${helpers.predefined_type(
- "page",
- "PageName",
- "computed::PageName::auto()",
- engines="gecko",
- spec="https://drafts.csswg.org/css-page-3/#using-named-pages",
- animation_value_type="discrete",
-)}
-
-${helpers.predefined_type(
- "page-orientation",
- "PageOrientation",
- "computed::PageOrientation::Upright",
- engines="gecko",
- gecko_pref="layout.css.page-orientation.enabled",
- initial_specified_value="specified::PageOrientation::Upright",
- spec="https://drafts.csswg.org/css-page-3/#page-orientation-prop",
- animation_value_type="none",
- rule_types_allowed=PAGE_RULE,
-)}
diff --git a/components/style/properties/longhands/position.mako.rs b/components/style/properties/longhands/position.mako.rs
deleted file mode 100644
index f7241f70d21..00000000000
--- a/components/style/properties/longhands/position.mako.rs
+++ /dev/null
@@ -1,478 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
-
-<%! from data import to_rust_ident %>
-<%namespace name="helpers" file="/helpers.mako.rs" />
-<% from data import ALL_SIZES, PHYSICAL_SIDES, LOGICAL_SIDES %>
-
-<% data.new_style_struct("Position", inherited=False) %>
-
-// "top" / "left" / "bottom" / "right"
-% for side in PHYSICAL_SIDES:
- ${helpers.predefined_type(
- side,
- "LengthPercentageOrAuto",
- "computed::LengthPercentageOrAuto::auto()",
- engines="gecko servo",
- spec="https://www.w3.org/TR/CSS2/visuren.html#propdef-%s" % side,
- animation_value_type="ComputedValue",
- allow_quirks="Yes",
- servo_restyle_damage="reflow_out_of_flow",
- logical_group="inset",
- )}
-% endfor
-// inset-* logical properties, map to "top" / "left" / "bottom" / "right"
-% for side in LOGICAL_SIDES:
- ${helpers.predefined_type(
- "inset-%s" % side,
- "LengthPercentageOrAuto",
- "computed::LengthPercentageOrAuto::auto()",
- engines="gecko servo",
- spec="https://drafts.csswg.org/css-logical-props/#propdef-inset-%s" % side,
- animation_value_type="ComputedValue",
- logical=True,
- logical_group="inset",
- )}
-% endfor
-
-#[cfg(feature = "gecko")]
-macro_rules! impl_align_conversions {
- ($name: path) => {
- impl From<u8> for $name {
- fn from(bits: u8) -> $name {
- $name(crate::values::specified::align::AlignFlags::from_bits(bits)
- .expect("bits contain valid flag"))
- }
- }
-
- impl From<$name> for u8 {
- fn from(v: $name) -> u8 {
- v.0.bits()
- }
- }
- };
-}
-
-${helpers.predefined_type(
- "z-index",
- "ZIndex",
- "computed::ZIndex::auto()",
- engines="gecko servo",
- spec="https://www.w3.org/TR/CSS2/visuren.html#z-index",
- animation_value_type="ComputedValue",
-)}
-
-// CSS Flexible Box Layout Module Level 1
-// http://www.w3.org/TR/css3-flexbox/
-
-// Flex container properties
-${helpers.single_keyword(
- "flex-direction",
- "row row-reverse column column-reverse",
- engines="gecko servo",
- servo_pref="layout.flexbox.enabled",
- spec="https://drafts.csswg.org/css-flexbox/#flex-direction-property",
- extra_prefixes="webkit",
- animation_value_type="discrete",
- servo_restyle_damage = "reflow",
- gecko_enum_prefix = "StyleFlexDirection",
-)}
-
-${helpers.single_keyword(
- "flex-wrap",
- "nowrap wrap wrap-reverse",
- engines="gecko servo",
- servo_pref="layout.flexbox.enabled",
- spec="https://drafts.csswg.org/css-flexbox/#flex-wrap-property",
- extra_prefixes="webkit",
- animation_value_type="discrete",
- servo_restyle_damage = "reflow",
- gecko_enum_prefix = "StyleFlexWrap",
-)}
-
-% if engine in "servo":
- // FIXME: Update Servo to support the same Syntax as Gecko.
- ${helpers.single_keyword(
- "justify-content",
- "flex-start stretch flex-end center space-between space-around",
- engines="servo",
- servo_pref="layout.flexbox.enabled",
- extra_prefixes="webkit",
- spec="https://drafts.csswg.org/css-align/#propdef-justify-content",
- animation_value_type="discrete",
- servo_restyle_damage = "reflow",
- )}
-% endif
-% if engine == "gecko":
- ${helpers.predefined_type(
- "justify-content",
- "JustifyContent",
- "specified::JustifyContent(specified::ContentDistribution::normal())",
- engines="gecko",
- spec="https://drafts.csswg.org/css-align/#propdef-justify-content",
- extra_prefixes="webkit",
- animation_value_type="discrete",
- servo_restyle_damage="reflow",
- )}
-
- ${helpers.predefined_type(
- "justify-tracks",
- "JustifyTracks",
- "specified::JustifyTracks::default()",
- engines="gecko",
- gecko_pref="layout.css.grid-template-masonry-value.enabled",
- animation_value_type="discrete",
- servo_restyle_damage="reflow",
- spec="https://github.com/w3c/csswg-drafts/issues/4650",
- )}
-% endif
-
-% if engine == "servo":
- // FIXME: Update Servo to support the same Syntax as Gecko.
- ${helpers.single_keyword(
- "align-content",
- "stretch flex-start flex-end center space-between space-around",
- engines="servo",
- servo_pref="layout.flexbox.enabled",
- extra_prefixes="webkit",
- spec="https://drafts.csswg.org/css-align/#propdef-align-content",
- animation_value_type="discrete",
- servo_restyle_damage="reflow",
- )}
-
- ${helpers.single_keyword(
- "align-items",
- "stretch flex-start flex-end center baseline",
- engines="servo",
- servo_pref="layout.flexbox.enabled",
- extra_prefixes="webkit",
- spec="https://drafts.csswg.org/css-flexbox/#align-items-property",
- animation_value_type="discrete",
- servo_restyle_damage="reflow",
- )}
-% endif
-% if engine == "gecko":
- ${helpers.predefined_type(
- "align-content",
- "AlignContent",
- "specified::AlignContent(specified::ContentDistribution::normal())",
- engines="gecko",
- spec="https://drafts.csswg.org/css-align/#propdef-align-content",
- extra_prefixes="webkit",
- animation_value_type="discrete",
- servo_restyle_damage="reflow",
- )}
-
- ${helpers.predefined_type(
- "align-tracks",
- "AlignTracks",
- "specified::AlignTracks::default()",
- engines="gecko",
- gecko_pref="layout.css.grid-template-masonry-value.enabled",
- animation_value_type="discrete",
- servo_restyle_damage="reflow",
- spec="https://github.com/w3c/csswg-drafts/issues/4650",
- )}
-
- ${helpers.predefined_type(
- "align-items",
- "AlignItems",
- "specified::AlignItems::normal()",
- engines="gecko",
- spec="https://drafts.csswg.org/css-align/#propdef-align-items",
- extra_prefixes="webkit",
- animation_value_type="discrete",
- servo_restyle_damage="reflow",
- )}
-
- #[cfg(feature = "gecko")]
- impl_align_conversions!(crate::values::specified::align::AlignItems);
-
- ${helpers.predefined_type(
- "justify-items",
- "JustifyItems",
- "computed::JustifyItems::legacy()",
- engines="gecko",
- spec="https://drafts.csswg.org/css-align/#propdef-justify-items",
- animation_value_type="discrete",
- )}
-
- #[cfg(feature = "gecko")]
- impl_align_conversions!(crate::values::specified::align::JustifyItems);
-% endif
-
-// Flex item properties
-${helpers.predefined_type(
- "flex-grow",
- "NonNegativeNumber",
- "From::from(0.0)",
- engines="gecko servo",
- servo_pref="layout.flexbox.enabled",
- spec="https://drafts.csswg.org/css-flexbox/#flex-grow-property",
- extra_prefixes="webkit",
- animation_value_type="NonNegativeNumber",
- servo_restyle_damage="reflow",
-)}
-
-${helpers.predefined_type(
- "flex-shrink",
- "NonNegativeNumber",
- "From::from(1.0)",
- engines="gecko servo",
- servo_pref="layout.flexbox.enabled",
- spec="https://drafts.csswg.org/css-flexbox/#flex-shrink-property",
- extra_prefixes="webkit",
- animation_value_type="NonNegativeNumber",
- servo_restyle_damage = "reflow",
-)}
-
-// https://drafts.csswg.org/css-align/#align-self-property
-% if engine == "servo":
- // FIXME: Update Servo to support the same syntax as Gecko.
- ${helpers.single_keyword(
- "align-self",
- "auto stretch flex-start flex-end center baseline",
- engines="servo",
- servo_pref="layout.flexbox.enabled",
- extra_prefixes="webkit",
- spec="https://drafts.csswg.org/css-flexbox/#propdef-align-self",
- animation_value_type="discrete",
- servo_restyle_damage = "reflow",
- )}
-% endif
-% if engine == "gecko":
- ${helpers.predefined_type(
- "align-self",
- "AlignSelf",
- "specified::AlignSelf(specified::SelfAlignment::auto())",
- engines="gecko",
- spec="https://drafts.csswg.org/css-align/#align-self-property",
- extra_prefixes="webkit",
- animation_value_type="discrete",
- )}
-
- ${helpers.predefined_type(
- "justify-self",
- "JustifySelf",
- "specified::JustifySelf(specified::SelfAlignment::auto())",
- engines="gecko",
- spec="https://drafts.csswg.org/css-align/#justify-self-property",
- animation_value_type="discrete",
- )}
-
- #[cfg(feature = "gecko")]
- impl_align_conversions!(crate::values::specified::align::SelfAlignment);
-% endif
-
-// https://drafts.csswg.org/css-flexbox/#propdef-order
-${helpers.predefined_type(
- "order",
- "Integer",
- "0",
- engines="gecko servo",
- servo_pref="layout.flexbox.enabled",
- extra_prefixes="webkit",
- animation_value_type="ComputedValue",
- spec="https://drafts.csswg.org/css-flexbox/#order-property",
- servo_restyle_damage="reflow",
-)}
-
-${helpers.predefined_type(
- "flex-basis",
- "FlexBasis",
- "computed::FlexBasis::auto()",
- engines="gecko servo",
- servo_pref="layout.flexbox.enabled",
- spec="https://drafts.csswg.org/css-flexbox/#flex-basis-property",
- extra_prefixes="webkit",
- animation_value_type="FlexBasis",
- servo_restyle_damage="reflow",
- boxed=True,
-)}
-
-% for (size, logical) in ALL_SIZES:
- <%
- spec = "https://drafts.csswg.org/css-box/#propdef-%s"
- if logical:
- spec = "https://drafts.csswg.org/css-logical-props/#propdef-%s"
- %>
- // width, height, block-size, inline-size
- ${helpers.predefined_type(
- size,
- "Size",
- "computed::Size::auto()",
- engines="gecko servo",
- logical=logical,
- logical_group="size",
- allow_quirks="No" if logical else "Yes",
- spec=spec % size,
- animation_value_type="Size",
- servo_restyle_damage="reflow",
- )}
- // min-width, min-height, min-block-size, min-inline-size
- ${helpers.predefined_type(
- "min-%s" % size,
- "Size",
- "computed::Size::auto()",
- engines="gecko servo",
- logical=logical,
- logical_group="min-size",
- allow_quirks="No" if logical else "Yes",
- spec=spec % size,
- animation_value_type="Size",
- servo_restyle_damage="reflow",
- )}
- ${helpers.predefined_type(
- "max-%s" % size,
- "MaxSize",
- "computed::MaxSize::none()",
- engines="gecko servo",
- logical=logical,
- logical_group="max-size",
- allow_quirks="No" if logical else "Yes",
- spec=spec % size,
- animation_value_type="MaxSize",
- servo_restyle_damage="reflow",
- )}
-% endfor
-
-${helpers.single_keyword(
- "box-sizing",
- "content-box border-box",
- engines="gecko servo",
- extra_prefixes="moz:layout.css.prefixes.box-sizing webkit",
- spec="https://drafts.csswg.org/css-ui/#propdef-box-sizing",
- gecko_enum_prefix="StyleBoxSizing",
- custom_consts={ "content-box": "Content", "border-box": "Border" },
- animation_value_type="discrete",
- servo_restyle_damage = "reflow",
-)}
-
-${helpers.single_keyword(
- "object-fit",
- "fill contain cover none scale-down",
- engines="gecko",
- animation_value_type="discrete",
- spec="https://drafts.csswg.org/css-images/#propdef-object-fit",
- gecko_enum_prefix = "StyleObjectFit",
-)}
-
-${helpers.predefined_type(
- "object-position",
- "Position",
- "computed::Position::center()",
- engines="gecko",
- boxed=True,
- spec="https://drafts.csswg.org/css-images-3/#the-object-position",
- animation_value_type="ComputedValue",
-)}
-
-% for kind in ["row", "column"]:
- % for range in ["start", "end"]:
- ${helpers.predefined_type(
- "grid-%s-%s" % (kind, range),
- "GridLine",
- "Default::default()",
- engines="gecko",
- animation_value_type="discrete",
- spec="https://drafts.csswg.org/css-grid/#propdef-grid-%s-%s" % (kind, range),
- )}
- % endfor
-
- ${helpers.predefined_type(
- "grid-auto-%ss" % kind,
- "ImplicitGridTracks",
- "Default::default()",
- engines="gecko",
- animation_value_type="discrete",
- spec="https://drafts.csswg.org/css-grid/#propdef-grid-auto-%ss" % kind,
- )}
-
- ${helpers.predefined_type(
- "grid-template-%ss" % kind,
- "GridTemplateComponent",
- "specified::GenericGridTemplateComponent::None",
- engines="gecko",
- spec="https://drafts.csswg.org/css-grid/#propdef-grid-template-%ss" % kind,
- animation_value_type="ComputedValue",
- )}
-
-% endfor
-
-${helpers.predefined_type(
- "masonry-auto-flow",
- "MasonryAutoFlow",
- "computed::MasonryAutoFlow::initial()",
- engines="gecko",
- gecko_pref="layout.css.grid-template-masonry-value.enabled",
- animation_value_type="discrete",
- spec="https://github.com/w3c/csswg-drafts/issues/4650",
-)}
-
-${helpers.predefined_type(
- "grid-auto-flow",
- "GridAutoFlow",
- "computed::GridAutoFlow::ROW",
- engines="gecko",
- animation_value_type="discrete",
- spec="https://drafts.csswg.org/css-grid/#propdef-grid-auto-flow",
-)}
-
-${helpers.predefined_type(
- "grid-template-areas",
- "GridTemplateAreas",
- "computed::GridTemplateAreas::none()",
- engines="gecko",
- animation_value_type="discrete",
- spec="https://drafts.csswg.org/css-grid/#propdef-grid-template-areas",
-)}
-
-${helpers.predefined_type(
- "column-gap",
- "length::NonNegativeLengthPercentageOrNormal",
- "computed::length::NonNegativeLengthPercentageOrNormal::normal()",
- engines="gecko servo",
- aliases="grid-column-gap" if engine == "gecko" else "",
- servo_pref="layout.columns.enabled",
- spec="https://drafts.csswg.org/css-align-3/#propdef-column-gap",
- animation_value_type="NonNegativeLengthPercentageOrNormal",
- servo_restyle_damage="reflow",
-)}
-
-// no need for -moz- prefixed alias for this property
-${helpers.predefined_type(
- "row-gap",
- "length::NonNegativeLengthPercentageOrNormal",
- "computed::length::NonNegativeLengthPercentageOrNormal::normal()",
- engines="gecko",
- aliases="grid-row-gap",
- spec="https://drafts.csswg.org/css-align-3/#propdef-row-gap",
- animation_value_type="NonNegativeLengthPercentageOrNormal",
- servo_restyle_damage="reflow",
-)}
-
-${helpers.predefined_type(
- "aspect-ratio",
- "AspectRatio",
- "computed::AspectRatio::auto()",
- engines="gecko servo",
- servo_pref="layout.legacy_layout",
- animation_value_type="ComputedValue",
- spec="https://drafts.csswg.org/css-sizing-4/#aspect-ratio",
- servo_restyle_damage="reflow",
-)}
-
-% for (size, logical) in ALL_SIZES:
- ${helpers.predefined_type(
- "contain-intrinsic-" + size,
- "ContainIntrinsicSize",
- "computed::ContainIntrinsicSize::None",
- engines="gecko",
- logical_group="contain-intrinsic-size",
- logical=logical,
- gecko_pref="layout.css.contain-intrinsic-size.enabled",
- spec="https://drafts.csswg.org/css-sizing-4/#intrinsic-size-override",
- animation_value_type="NonNegativeLength",
- )}
-% endfor
diff --git a/components/style/properties/longhands/svg.mako.rs b/components/style/properties/longhands/svg.mako.rs
deleted file mode 100644
index d579473a023..00000000000
--- a/components/style/properties/longhands/svg.mako.rs
+++ /dev/null
@@ -1,258 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
-
-<%namespace name="helpers" file="/helpers.mako.rs" />
-
-<% data.new_style_struct("SVG", inherited=False, gecko_name="SVGReset") %>
-
-${helpers.single_keyword(
- "vector-effect",
- "none non-scaling-stroke",
- engines="gecko",
- gecko_enum_prefix="StyleVectorEffect",
- animation_value_type="discrete",
- spec="https://www.w3.org/TR/SVGTiny12/painting.html#VectorEffectProperty",
-)}
-
-// Section 13 - Gradients and Patterns
-
-${helpers.predefined_type(
- "stop-color",
- "Color",
- "computed::Color::black()",
- engines="gecko",
- animation_value_type="AnimatedRGBA",
- spec="https://www.w3.org/TR/SVGTiny12/painting.html#StopColorProperty",
-)}
-
-${helpers.predefined_type(
- "stop-opacity",
- "Opacity",
- "1.0",
- engines="gecko",
- animation_value_type="ComputedValue",
- spec="https://svgwg.org/svg2-draft/pservers.html#StopOpacityProperty",
-)}
-
-// Section 15 - Filter Effects
-
-${helpers.predefined_type(
- "flood-color",
- "Color",
- "computed::Color::black()",
- engines="gecko",
- animation_value_type="AnimatedColor",
- spec="https://www.w3.org/TR/SVG/filters.html#FloodColorProperty",
-)}
-
-${helpers.predefined_type(
- "flood-opacity",
- "Opacity",
- "1.0",
- engines="gecko",
- animation_value_type="ComputedValue",
- spec="https://drafts.fxtf.org/filter-effects/#FloodOpacityProperty",
-)}
-
-${helpers.predefined_type(
- "lighting-color",
- "Color",
- "computed::Color::white()",
- engines="gecko",
- animation_value_type="AnimatedColor",
- spec="https://www.w3.org/TR/SVG/filters.html#LightingColorProperty",
-)}
-
-// CSS Masking Module Level 1
-// https://drafts.fxtf.org/css-masking
-${helpers.single_keyword(
- "mask-type",
- "luminance alpha",
- engines="gecko",
- gecko_enum_prefix="StyleMaskType",
- animation_value_type="discrete",
- spec="https://drafts.fxtf.org/css-masking/#propdef-mask-type",
-)}
-
-${helpers.predefined_type(
- "clip-path",
- "basic_shape::ClipPath",
- "generics::basic_shape::ClipPath::None",
- engines="gecko",
- extra_prefixes="webkit",
- animation_value_type="basic_shape::ClipPath",
- spec="https://drafts.fxtf.org/css-masking/#propdef-clip-path",
-)}
-
-${helpers.single_keyword(
- "mask-mode",
- "match-source alpha luminance",
- engines="gecko",
- gecko_enum_prefix="StyleMaskMode",
- vector=True,
- animation_value_type="discrete",
- spec="https://drafts.fxtf.org/css-masking/#propdef-mask-mode",
-)}
-
-${helpers.predefined_type(
- "mask-repeat",
- "BackgroundRepeat",
- "computed::BackgroundRepeat::repeat()",
- engines="gecko",
- initial_specified_value="specified::BackgroundRepeat::repeat()",
- extra_prefixes="webkit",
- animation_value_type="discrete",
- spec="https://drafts.fxtf.org/css-masking/#propdef-mask-repeat",
- vector=True,
-)}
-
-% for (axis, direction) in [("x", "Horizontal"), ("y", "Vertical")]:
- ${helpers.predefined_type(
- "mask-position-" + axis,
- "position::" + direction + "Position",
- "computed::LengthPercentage::zero_percent()",
- engines="gecko",
- extra_prefixes="webkit",
- initial_specified_value="specified::PositionComponent::Center",
- spec="https://drafts.fxtf.org/css-masking/#propdef-mask-position",
- animation_value_type="ComputedValue",
- vector_animation_type="repeatable_list",
- vector=True,
- )}
-% endfor
-
-${helpers.single_keyword(
- "mask-clip",
- "border-box content-box padding-box",
- engines="gecko",
- extra_gecko_values="fill-box stroke-box view-box no-clip",
- vector=True,
- extra_prefixes="webkit",
- gecko_enum_prefix="StyleGeometryBox",
- gecko_inexhaustive=True,
- animation_value_type="discrete",
- spec="https://drafts.fxtf.org/css-masking/#propdef-mask-clip",
-)}
-
-${helpers.single_keyword(
- "mask-origin",
- "border-box content-box padding-box",
- engines="gecko",
- extra_gecko_values="fill-box stroke-box view-box",
- vector=True,
- extra_prefixes="webkit",
- gecko_enum_prefix="StyleGeometryBox",
- gecko_inexhaustive=True,
- animation_value_type="discrete",
- spec="https://drafts.fxtf.org/css-masking/#propdef-mask-origin",
-)}
-
-${helpers.predefined_type(
- "mask-size",
- "background::BackgroundSize",
- "computed::BackgroundSize::auto()",
- engines="gecko",
- initial_specified_value="specified::BackgroundSize::auto()",
- extra_prefixes="webkit",
- spec="https://drafts.fxtf.org/css-masking/#propdef-mask-size",
- animation_value_type="MaskSizeList",
- vector=True,
- vector_animation_type="repeatable_list",
-)}
-
-${helpers.single_keyword(
- "mask-composite",
- "add subtract intersect exclude",
- engines="gecko",
- gecko_enum_prefix="StyleMaskComposite",
- vector=True,
- extra_prefixes="webkit",
- animation_value_type="discrete",
- spec="https://drafts.fxtf.org/css-masking/#propdef-mask-composite",
-)}
-
-${helpers.predefined_type(
- "mask-image",
- "Image",
- engines="gecko",
- initial_value="computed::Image::None",
- initial_specified_value="specified::Image::None",
- parse_method="parse_with_cors_anonymous",
- spec="https://drafts.fxtf.org/css-masking/#propdef-mask-image",
- vector=True,
- extra_prefixes="webkit",
- animation_value_type="discrete",
-)}
-
-${helpers.predefined_type(
- "x",
- "LengthPercentage",
- "computed::LengthPercentage::zero()",
- engines="gecko",
- animation_value_type="ComputedValue",
- spec="https://svgwg.org/svg2-draft/geometry.html#X",
-)}
-
-${helpers.predefined_type(
- "y",
- "LengthPercentage",
- "computed::LengthPercentage::zero()",
- engines="gecko",
- animation_value_type="ComputedValue",
- spec="https://svgwg.org/svg2-draft/geometry.html#Y",
-)}
-
-${helpers.predefined_type(
- "cx",
- "LengthPercentage",
- "computed::LengthPercentage::zero()",
- engines="gecko",
- animation_value_type="ComputedValue",
- spec="https://svgwg.org/svg2-draft/geometry.html#CX",
-)}
-
-${helpers.predefined_type(
- "cy",
- "LengthPercentage",
- "computed::LengthPercentage::zero()",
- engines="gecko",
- animation_value_type="ComputedValue",
- spec="https://svgwg.org/svg2-draft/geometry.html#CY",
-)}
-
-${helpers.predefined_type(
- "rx",
- "NonNegativeLengthPercentageOrAuto",
- "computed::NonNegativeLengthPercentageOrAuto::auto()",
- engines="gecko",
- animation_value_type="LengthPercentageOrAuto",
- spec="https://svgwg.org/svg2-draft/geometry.html#RX",
-)}
-
-${helpers.predefined_type(
- "ry",
- "NonNegativeLengthPercentageOrAuto",
- "computed::NonNegativeLengthPercentageOrAuto::auto()",
- engines="gecko",
- animation_value_type="LengthPercentageOrAuto",
- spec="https://svgwg.org/svg2-draft/geometry.html#RY",
-)}
-
-${helpers.predefined_type(
- "r",
- "NonNegativeLengthPercentage",
- "computed::NonNegativeLengthPercentage::zero()",
- engines="gecko",
- animation_value_type="LengthPercentage",
- spec="https://svgwg.org/svg2-draft/geometry.html#R",
-)}
-
-${helpers.predefined_type(
- "d",
- "DProperty",
- "specified::DProperty::none()",
- engines="gecko",
- animation_value_type="ComputedValue",
- spec="https://svgwg.org/svg2-draft/paths.html#TheDProperty",
-)}
diff --git a/components/style/properties/longhands/table.mako.rs b/components/style/properties/longhands/table.mako.rs
deleted file mode 100644
index 73fdd51ffbf..00000000000
--- a/components/style/properties/longhands/table.mako.rs
+++ /dev/null
@@ -1,29 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
-
-<%namespace name="helpers" file="/helpers.mako.rs" />
-
-<% data.new_style_struct("Table", inherited=False) %>
-
-${helpers.single_keyword(
- "table-layout",
- "auto fixed",
- engines="gecko servo",
- servo_pref="layout.legacy_layout",
- gecko_ffi_name="mLayoutStrategy",
- animation_value_type="discrete",
- gecko_enum_prefix="StyleTableLayout",
- spec="https://drafts.csswg.org/css-tables/#propdef-table-layout",
- servo_restyle_damage="reflow",
-)}
-
-${helpers.predefined_type(
- "-x-span",
- "Integer",
- "1",
- engines="gecko",
- spec="Internal-only (for `<col span>` pres attr)",
- animation_value_type="none",
- enabled_in="",
-)}
diff --git a/components/style/properties/longhands/text.mako.rs b/components/style/properties/longhands/text.mako.rs
deleted file mode 100644
index 30471821b9b..00000000000
--- a/components/style/properties/longhands/text.mako.rs
+++ /dev/null
@@ -1,83 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
-
-<%namespace name="helpers" file="/helpers.mako.rs" />
-<% from data import Method %>
-
-<% data.new_style_struct("Text", inherited=False, gecko_name="TextReset") %>
-
-${helpers.predefined_type(
- "text-overflow",
- "TextOverflow",
- "computed::TextOverflow::get_initial_value()",
- engines="gecko servo",
- servo_pref="layout.legacy_layout",
- animation_value_type="discrete",
- boxed=True,
- spec="https://drafts.csswg.org/css-ui/#propdef-text-overflow",
- servo_restyle_damage="rebuild_and_reflow",
-)}
-
-${helpers.single_keyword(
- "unicode-bidi",
- "normal embed isolate bidi-override isolate-override plaintext",
- engines="gecko servo",
- servo_pref="layout.legacy_layout",
- gecko_enum_prefix="StyleUnicodeBidi",
- animation_value_type="none",
- spec="https://drafts.csswg.org/css-writing-modes/#propdef-unicode-bidi",
- servo_restyle_damage="rebuild_and_reflow",
-)}
-
-${helpers.predefined_type(
- "text-decoration-line",
- "TextDecorationLine",
- "specified::TextDecorationLine::none()",
- engines="gecko servo",
- initial_specified_value="specified::TextDecorationLine::none()",
- animation_value_type="discrete",
- spec="https://drafts.csswg.org/css-text-decor/#propdef-text-decoration-line",
- servo_restyle_damage="rebuild_and_reflow",
-)}
-
-${helpers.single_keyword(
- "text-decoration-style",
- "solid double dotted dashed wavy -moz-none",
- engines="gecko servo",
- gecko_enum_prefix="StyleTextDecorationStyle",
- animation_value_type="discrete",
- spec="https://drafts.csswg.org/css-text-decor/#propdef-text-decoration-style",
-)}
-
-${helpers.predefined_type(
- "text-decoration-color",
- "Color",
- "computed_value::T::currentcolor()",
- engines="gecko servo",
- initial_specified_value="specified::Color::currentcolor()",
- animation_value_type="AnimatedColor",
- ignored_when_colors_disabled=True,
- spec="https://drafts.csswg.org/css-text-decor/#propdef-text-decoration-color",
-)}
-
-${helpers.predefined_type(
- "initial-letter",
- "InitialLetter",
- "computed::InitialLetter::normal()",
- engines="gecko",
- initial_specified_value="specified::InitialLetter::normal()",
- animation_value_type="discrete",
- gecko_pref="layout.css.initial-letter.enabled",
- spec="https://drafts.csswg.org/css-inline/#sizing-drop-initials",
-)}
-
-${helpers.predefined_type(
- "text-decoration-thickness",
- "TextDecorationLength",
- "generics::text::GenericTextDecorationLength::Auto",
- engines="gecko",
- initial_specified_value="generics::text::GenericTextDecorationLength::Auto",
- animation_value_type="ComputedValue",
- spec="https://drafts.csswg.org/css-text-decor-4/#text-decoration-width-property"
-)}
diff --git a/components/style/properties/longhands/ui.mako.rs b/components/style/properties/longhands/ui.mako.rs
deleted file mode 100644
index 80724d6e5d3..00000000000
--- a/components/style/properties/longhands/ui.mako.rs
+++ /dev/null
@@ -1,397 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
-
-<%namespace name="helpers" file="/helpers.mako.rs" />
-<% from data import DEFAULT_RULES_EXCEPT_KEYFRAME, Method %>
-
-// CSS Basic User Interface Module Level 1
-// https://drafts.csswg.org/css-ui-3/
-<% data.new_style_struct("UI", inherited=False, gecko_name="UIReset") %>
-
-// TODO spec says that UAs should not support this
-// we should probably remove from gecko (https://bugzilla.mozilla.org/show_bug.cgi?id=1328331)
-${helpers.single_keyword(
- "ime-mode",
- "auto normal active disabled inactive",
- engines="gecko",
- gecko_enum_prefix="StyleImeMode",
- gecko_ffi_name="mIMEMode",
- animation_value_type="discrete",
- spec="https://drafts.csswg.org/css-ui/#input-method-editor",
-)}
-
-${helpers.single_keyword(
- "scrollbar-width",
- "auto thin none",
- engines="gecko",
- gecko_enum_prefix="StyleScrollbarWidth",
- animation_value_type="discrete",
- spec="https://drafts.csswg.org/css-scrollbars-1/#scrollbar-width"
-)}
-
-${helpers.predefined_type(
- "user-select",
- "UserSelect",
- "computed::UserSelect::Auto",
- engines="gecko",
- extra_prefixes="moz webkit",
- animation_value_type="discrete",
- spec="https://drafts.csswg.org/css-ui-4/#propdef-user-select",
-)}
-
-// TODO(emilio): This probably should be hidden from content.
-${helpers.single_keyword(
- "-moz-window-dragging",
- "default drag no-drag",
- engines="gecko",
- gecko_ffi_name="mWindowDragging",
- gecko_enum_prefix="StyleWindowDragging",
- animation_value_type="discrete",
- spec="None (Nonstandard Firefox-only property)",
-)}
-
-// TODO(emilio): Maybe make shadow behavior on macOS match Linux / Windows, and remove this
-// property.
-${helpers.single_keyword(
- "-moz-window-shadow",
- "default none",
- engines="gecko",
- gecko_ffi_name="mWindowShadow",
- gecko_enum_prefix="StyleWindowShadow",
- gecko_inexhaustive=True,
- animation_value_type="discrete",
- enabled_in="chrome",
- spec="None (Nonstandard internal property)",
-)}
-
-${helpers.predefined_type(
- "-moz-window-opacity",
- "Opacity",
- "1.0",
- engines="gecko",
- gecko_ffi_name="mWindowOpacity",
- animation_value_type="ComputedValue",
- spec="None (Nonstandard internal property)",
- enabled_in="chrome",
-)}
-
-${helpers.predefined_type(
- "-moz-window-transform",
- "Transform",
- "generics::transform::Transform::none()",
- engines="gecko",
- animation_value_type="ComputedValue",
- spec="None (Nonstandard internal property)",
- enabled_in="chrome",
-)}
-
-${helpers.predefined_type(
- "-moz-window-transform-origin",
- "TransformOrigin",
- "computed::TransformOrigin::initial_value()",
- engines="gecko",
- animation_value_type="ComputedValue",
- gecko_ffi_name="mWindowTransformOrigin",
- boxed=True,
- spec="None (Nonstandard internal property)",
- enabled_in="chrome",
-)}
-
-${helpers.predefined_type(
- "-moz-window-input-region-margin",
- "Length",
- "computed::Length::zero()",
- engines="gecko",
- animation_value_type="ComputedValue",
- spec="None (Nonstandard internal property)",
- enabled_in="chrome",
-)}
-
-// Hack to allow chrome to hide stuff only visually (without hiding it from
-// a11y).
-${helpers.predefined_type(
- "-moz-subtree-hidden-only-visually",
- "BoolInteger",
- "computed::BoolInteger::zero()",
- engines="gecko",
- animation_value_type="discrete",
- spec="None (Nonstandard internal property)",
- enabled_in="chrome",
-)}
-
-// TODO(emilio): Probably also should be hidden from content.
-${helpers.predefined_type(
- "-moz-force-broken-image-icon",
- "BoolInteger",
- "computed::BoolInteger::zero()",
- engines="gecko",
- animation_value_type="discrete",
- spec="None (Nonstandard Firefox-only property)",
-)}
-
-<% transition_extra_prefixes = "moz:layout.css.prefixes.transitions webkit" %>
-
-${helpers.predefined_type(
- "transition-duration",
- "Time",
- "computed::Time::zero()",
- engines="gecko servo",
- initial_specified_value="specified::Time::zero()",
- parse_method="parse_non_negative",
- vector=True,
- need_index=True,
- animation_value_type="none",
- extra_prefixes=transition_extra_prefixes,
- spec="https://drafts.csswg.org/css-transitions/#propdef-transition-duration",
-)}
-
-${helpers.predefined_type(
- "transition-timing-function",
- "TimingFunction",
- "computed::TimingFunction::ease()",
- engines="gecko servo",
- initial_specified_value="specified::TimingFunction::ease()",
- vector=True,
- need_index=True,
- animation_value_type="none",
- extra_prefixes=transition_extra_prefixes,
- spec="https://drafts.csswg.org/css-transitions/#propdef-transition-timing-function",
-)}
-
-${helpers.predefined_type(
- "transition-property",
- "TransitionProperty",
- "computed::TransitionProperty::all()",
- engines="gecko servo",
- initial_specified_value="specified::TransitionProperty::all()",
- vector=True,
- allow_empty="NotInitial",
- need_index=True,
- animation_value_type="none",
- extra_prefixes=transition_extra_prefixes,
- spec="https://drafts.csswg.org/css-transitions/#propdef-transition-property",
-)}
-
-${helpers.predefined_type(
- "transition-delay",
- "Time",
- "computed::Time::zero()",
- engines="gecko servo",
- initial_specified_value="specified::Time::zero()",
- vector=True,
- need_index=True,
- animation_value_type="none",
- extra_prefixes=transition_extra_prefixes,
- spec="https://drafts.csswg.org/css-transitions/#propdef-transition-delay",
-)}
-
-<% animation_extra_prefixes = "moz:layout.css.prefixes.animations webkit" %>
-
-${helpers.predefined_type(
- "animation-name",
- "AnimationName",
- "computed::AnimationName::none()",
- engines="gecko servo",
- initial_specified_value="specified::AnimationName::none()",
- vector=True,
- need_index=True,
- animation_value_type="none",
- extra_prefixes=animation_extra_prefixes,
- rule_types_allowed=DEFAULT_RULES_EXCEPT_KEYFRAME,
- spec="https://drafts.csswg.org/css-animations/#propdef-animation-name",
-)}
-
-${helpers.predefined_type(
- "animation-duration",
- "Time",
- "computed::Time::zero()",
- engines="gecko servo",
- initial_specified_value="specified::Time::zero()",
- parse_method="parse_non_negative",
- vector=True,
- need_index=True,
- animation_value_type="none",
- extra_prefixes=animation_extra_prefixes,
- spec="https://drafts.csswg.org/css-transitions/#propdef-transition-duration",
-)}
-
-// animation-timing-function is the exception to the rule for allowed_in_keyframe_block:
-// https://drafts.csswg.org/css-animations/#keyframes
-${helpers.predefined_type(
- "animation-timing-function",
- "TimingFunction",
- "computed::TimingFunction::ease()",
- engines="gecko servo",
- initial_specified_value="specified::TimingFunction::ease()",
- vector=True,
- need_index=True,
- animation_value_type="none",
- extra_prefixes=animation_extra_prefixes,
- spec="https://drafts.csswg.org/css-transitions/#propdef-animation-timing-function",
-)}
-
-${helpers.predefined_type(
- "animation-iteration-count",
- "AnimationIterationCount",
- "computed::AnimationIterationCount::one()",
- engines="gecko servo",
- initial_specified_value="specified::AnimationIterationCount::one()",
- vector=True,
- need_index=True,
- animation_value_type="none",
- extra_prefixes=animation_extra_prefixes,
- rule_types_allowed=DEFAULT_RULES_EXCEPT_KEYFRAME,
- spec="https://drafts.csswg.org/css-animations/#propdef-animation-iteration-count",
-)}
-
-<% animation_direction_custom_consts = { "alternate-reverse": "Alternate_reverse" } %>
-${helpers.single_keyword(
- "animation-direction",
- "normal reverse alternate alternate-reverse",
- engines="gecko servo",
- need_index=True,
- animation_value_type="none",
- vector=True,
- gecko_enum_prefix="PlaybackDirection",
- custom_consts=animation_direction_custom_consts,
- extra_prefixes=animation_extra_prefixes,
- gecko_inexhaustive=True,
- spec="https://drafts.csswg.org/css-animations/#propdef-animation-direction",
- rule_types_allowed=DEFAULT_RULES_EXCEPT_KEYFRAME,
-)}
-
-${helpers.single_keyword(
- "animation-play-state",
- "running paused",
- engines="gecko servo",
- need_index=True,
- animation_value_type="none",
- vector=True,
- extra_prefixes=animation_extra_prefixes,
- gecko_enum_prefix="StyleAnimationPlayState",
- spec="https://drafts.csswg.org/css-animations/#propdef-animation-play-state",
- rule_types_allowed=DEFAULT_RULES_EXCEPT_KEYFRAME,
-)}
-
-${helpers.single_keyword(
- "animation-fill-mode",
- "none forwards backwards both",
- engines="gecko servo",
- need_index=True,
- animation_value_type="none",
- vector=True,
- gecko_enum_prefix="FillMode",
- extra_prefixes=animation_extra_prefixes,
- gecko_inexhaustive=True,
- spec="https://drafts.csswg.org/css-animations/#propdef-animation-fill-mode",
- rule_types_allowed=DEFAULT_RULES_EXCEPT_KEYFRAME,
-)}
-
-${helpers.single_keyword(
- "animation-composition",
- "replace add accumulate",
- engines="gecko servo",
- need_index=True,
- animation_value_type="none",
- vector=True,
- gecko_enum_prefix="CompositeOperation",
- gecko_inexhaustive=True,
- gecko_pref="layout.css.animation-composition.enabled",
- servo_pref="layout.unimplemented",
- spec="https://drafts.csswg.org/css-animations-2/#animation-composition",
-)}
-
-${helpers.predefined_type(
- "animation-delay",
- "Time",
- "computed::Time::zero()",
- engines="gecko servo",
- initial_specified_value="specified::Time::zero()",
- vector=True,
- need_index=True,
- animation_value_type="none",
- extra_prefixes=animation_extra_prefixes,
- spec="https://drafts.csswg.org/css-animations/#propdef-animation-delay",
- rule_types_allowed=DEFAULT_RULES_EXCEPT_KEYFRAME,
-)}
-
-${helpers.predefined_type(
- "animation-timeline",
- "AnimationTimeline",
- "computed::AnimationTimeline::auto()",
- engines="gecko servo",
- servo_pref="layout.unimplemented",
- initial_specified_value="specified::AnimationTimeline::auto()",
- vector=True,
- need_index=True,
- animation_value_type="none",
- gecko_pref="layout.css.scroll-driven-animations.enabled",
- spec="https://drafts.csswg.org/css-animations-2/#propdef-animation-timeline",
- rule_types_allowed=DEFAULT_RULES_EXCEPT_KEYFRAME,
-)}
-
-${helpers.predefined_type(
- "scroll-timeline-name",
- "ScrollTimelineName",
- "computed::ScrollTimelineName::none()",
- vector=True,
- need_index=True,
- engines="gecko",
- animation_value_type="none",
- gecko_pref="layout.css.scroll-driven-animations.enabled",
- spec="https://drafts.csswg.org/scroll-animations-1/#scroll-timeline-name",
- rule_types_allowed=DEFAULT_RULES_EXCEPT_KEYFRAME,
-)}
-
-${helpers.predefined_type(
- "scroll-timeline-axis",
- "ScrollAxis",
- "computed::ScrollAxis::default()",
- vector=True,
- need_index=True,
- engines="gecko",
- animation_value_type="none",
- gecko_pref="layout.css.scroll-driven-animations.enabled",
- spec="https://drafts.csswg.org/scroll-animations-1/#scroll-timeline-axis",
- rule_types_allowed=DEFAULT_RULES_EXCEPT_KEYFRAME,
-)}
-
-${helpers.predefined_type(
- "view-timeline-name",
- "ScrollTimelineName",
- "computed::ScrollTimelineName::none()",
- vector=True,
- need_index=True,
- engines="gecko",
- animation_value_type="none",
- gecko_pref="layout.css.scroll-driven-animations.enabled",
- spec="https://drafts.csswg.org/scroll-animations-1/#view-timeline-name",
- rule_types_allowed=DEFAULT_RULES_EXCEPT_KEYFRAME,
-)}
-
-${helpers.predefined_type(
- "view-timeline-axis",
- "ScrollAxis",
- "computed::ScrollAxis::default()",
- vector=True,
- need_index=True,
- engines="gecko",
- animation_value_type="none",
- gecko_pref="layout.css.scroll-driven-animations.enabled",
- spec="https://drafts.csswg.org/scroll-animations-1/#view-timeline-axis",
- rule_types_allowed=DEFAULT_RULES_EXCEPT_KEYFRAME,
-)}
-
-${helpers.predefined_type(
- "view-timeline-inset",
- "ViewTimelineInset",
- "computed::ViewTimelineInset::default()",
- vector=True,
- need_index=True,
- engines="gecko",
- animation_value_type="none",
- gecko_pref="layout.css.scroll-driven-animations.enabled",
- spec="https://drafts.csswg.org/scroll-animations-1/#view-timeline-axis",
- rule_types_allowed=DEFAULT_RULES_EXCEPT_KEYFRAME,
-)}
diff --git a/components/style/properties/longhands/xul.mako.rs b/components/style/properties/longhands/xul.mako.rs
deleted file mode 100644
index a939b018920..00000000000
--- a/components/style/properties/longhands/xul.mako.rs
+++ /dev/null
@@ -1,79 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
-
-<%namespace name="helpers" file="/helpers.mako.rs" />
-<% from data import Method %>
-
-// Non-standard properties that Gecko uses for XUL elements.
-<% data.new_style_struct("XUL", inherited=False) %>
-
-${helpers.single_keyword(
- "-moz-box-align",
- "stretch start center baseline end",
- engines="gecko",
- gecko_ffi_name="mBoxAlign",
- gecko_enum_prefix="StyleBoxAlign",
- animation_value_type="discrete",
- aliases="-webkit-box-align",
- spec="Nonstandard (https://developer.mozilla.org/en-US/docs/Web/CSS/box-align)",
-)}
-
-${helpers.single_keyword(
- "-moz-box-direction",
- "normal reverse",
- engines="gecko",
- gecko_ffi_name="mBoxDirection",
- gecko_enum_prefix="StyleBoxDirection",
- animation_value_type="discrete",
- aliases="-webkit-box-direction",
- spec="Nonstandard (https://developer.mozilla.org/en-US/docs/Web/CSS/box-direction)",
-)}
-
-${helpers.predefined_type(
- "-moz-box-flex",
- "NonNegativeNumber",
- "From::from(0.)",
- engines="gecko",
- gecko_ffi_name="mBoxFlex",
- animation_value_type="NonNegativeNumber",
- aliases="-webkit-box-flex",
- spec="Nonstandard (https://developer.mozilla.org/en-US/docs/Web/CSS/box-flex)",
-)}
-
-${helpers.single_keyword(
- "-moz-box-orient",
- "horizontal vertical",
- engines="gecko",
- gecko_ffi_name="mBoxOrient",
- gecko_aliases="inline-axis=horizontal block-axis=vertical",
- gecko_enum_prefix="StyleBoxOrient",
- animation_value_type="discrete",
- aliases="-webkit-box-orient",
- spec="Nonstandard (https://developer.mozilla.org/en-US/docs/Web/CSS/box-orient)",
-)}
-
-${helpers.single_keyword(
- "-moz-box-pack",
- "start center end justify",
- engines="gecko",
- gecko_ffi_name="mBoxPack",
- gecko_enum_prefix="StyleBoxPack",
- animation_value_type="discrete",
- aliases="-webkit-box-pack",
- spec="Nonstandard (https://developer.mozilla.org/en-US/docs/Web/CSS/box-pack)",
-)}
-
-// NOTE(heycam): Odd that the initial value is 1 yet 0 is a valid value. There
-// are uses of `-moz-box-ordinal-group: 0` in the tree, too.
-${helpers.predefined_type(
- "-moz-box-ordinal-group",
- "Integer",
- "1",
- engines="gecko",
- parse_method="parse_non_negative",
- aliases="-webkit-box-ordinal-group",
- gecko_ffi_name="mBoxOrdinal",
- animation_value_type="discrete",
- spec="Nonstandard (https://developer.mozilla.org/en-US/docs/Web/CSS/-moz-box-ordinal-group)",
-)}
diff --git a/components/style/properties/mod.rs b/components/style/properties/mod.rs
deleted file mode 100644
index 9eb567ca6f6..00000000000
--- a/components/style/properties/mod.rs
+++ /dev/null
@@ -1,27 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
-
-//! Supported CSS properties and the cascade.
-
-pub mod cascade;
-pub mod declaration_block;
-
-/// The CSS properties supported by the style system.
-/// Generated from the properties.mako.rs template by build.rs
-#[macro_use]
-#[allow(unsafe_code)]
-#[deny(missing_docs)]
-pub mod generated {
- include!(concat!(env!("OUT_DIR"), "/properties.rs"));
-
- #[cfg(feature = "gecko")]
- #[allow(unsafe_code, missing_docs)]
- pub mod gecko {
- include!(concat!(env!("OUT_DIR"), "/gecko_properties.rs"));
- }
-}
-
-pub use self::cascade::*;
-pub use self::declaration_block::*;
-pub use self::generated::*;
diff --git a/components/style/properties/properties.html.mako b/components/style/properties/properties.html.mako
deleted file mode 100644
index 5c515935175..00000000000
--- a/components/style/properties/properties.html.mako
+++ /dev/null
@@ -1,31 +0,0 @@
-<!DOCTYPE html>
-<html lang="en">
-<head>
- <meta charset="utf-8">
- <meta name="viewport" content="width=device-width, initial-scale=1.0">
- <title>Supported CSS properties in Servo</title>
- <link rel="stylesheet" type="text/css" href="../normalize.css">
- <link rel="stylesheet" type="text/css" href="../rustdoc.css">
- <link rel="stylesheet" type="text/css" href="../light.css">
-</head>
-<body class="rustdoc">
- <section id='main' class="content mod">
- <h1 class='fqn'><span class='in-band'>CSS properties currently supported in Servo</span></h1>
- % for kind, props in sorted(properties.items()):
- <h2>${kind.capitalize()}</h2>
- <table>
- <tr>
- <th>Name</th>
- <th>Pref</th>
- </tr>
- % for name, data in sorted(props.items()):
- <tr>
- <td><code>${name}</code></td>
- <td><code>${data['pref'] or ''}</code></td>
- </tr>
- % endfor
- </table>
- % endfor
- </section>
-</body>
-</html>
diff --git a/components/style/properties/properties.mako.rs b/components/style/properties/properties.mako.rs
deleted file mode 100644
index 4c33f9d7458..00000000000
--- a/components/style/properties/properties.mako.rs
+++ /dev/null
@@ -1,4277 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
-
-// This file is a Mako template: http://www.makotemplates.org/
-
-// Please note that valid Rust syntax may be mangled by the Mako parser.
-// For example, Vec<&Foo> will be mangled as Vec&Foo>. To work around these issues, the code
-// can be escaped. In the above example, Vec<<&Foo> or Vec< &Foo> achieves the desired result of Vec<&Foo>.
-
-<%namespace name="helpers" file="/helpers.mako.rs" />
-
-use app_units::Au;
-use arrayvec::{ArrayVec, Drain as ArrayVecDrain};
-use servo_arc::{Arc, UniqueArc};
-use std::borrow::Cow;
-use std::{ops, ptr};
-use std::fmt::{self, Write};
-use std::mem;
-
-use cssparser::{Parser, TokenSerializationType};
-use cssparser::ParserInput;
-#[cfg(feature = "servo")] use euclid::SideOffsets2D;
-use crate::context::QuirksMode;
-#[cfg(feature = "gecko")] use crate::gecko_bindings::structs::{self, nsCSSPropertyID};
-#[cfg(feature = "servo")] use crate::logical_geometry::LogicalMargin;
-#[cfg(feature = "servo")] use crate::computed_values;
-use crate::logical_geometry::WritingMode;
-use malloc_size_of::{MallocSizeOf, MallocSizeOfOps};
-use crate::computed_value_flags::*;
-use fxhash::FxHashMap;
-use crate::media_queries::Device;
-use crate::parser::ParserContext;
-use crate::selector_parser::PseudoElement;
-use style_traits::{CssWriter, KeywordsCollectFn, ParseError, ParsingMode};
-use style_traits::{SpecifiedValueInfo, StyleParseErrorKind, ToCss};
-use to_shmem::impl_trivial_to_shmem;
-use crate::stylesheets::{CssRuleType, CssRuleTypes, Origin, UrlExtraData};
-use crate::use_counters::UseCounters;
-use crate::values::generics::text::LineHeight;
-use crate::values::{computed, resolved, serialize_atom_name};
-use crate::values::specified::font::SystemFont;
-use crate::rule_tree::StrongRuleNode;
-use crate::str::{CssString, CssStringWriter};
-use std::cell::Cell;
-use super::declaration_block::AppendableValue;
-
-<%!
- from collections import defaultdict
- from data import Method, PropertyRestrictions, Keyword, to_rust_ident, \
- to_camel_case, RULE_VALUES, SYSTEM_FONT_LONGHANDS
- import os.path
-%>
-
-/// Conversion with fewer impls than From/Into
-pub trait MaybeBoxed<Out> {
- /// Convert
- fn maybe_boxed(self) -> Out;
-}
-
-impl<T> MaybeBoxed<T> for T {
- #[inline]
- fn maybe_boxed(self) -> T { self }
-}
-
-impl<T> MaybeBoxed<Box<T>> for T {
- #[inline]
- fn maybe_boxed(self) -> Box<T> { Box::new(self) }
-}
-
-macro_rules! expanded {
- ( $( $name: ident: $value: expr ),+ ) => {
- expanded!( $( $name: $value, )+ )
- };
- ( $( $name: ident: $value: expr, )+ ) => {
- Longhands {
- $(
- $name: MaybeBoxed::maybe_boxed($value),
- )+
- }
- }
-}
-
-/// A module with all the code for longhand properties.
-#[allow(missing_docs)]
-pub mod longhands {
- % for style_struct in data.style_structs:
- include!("${repr(os.path.join(OUT_DIR, 'longhands/{}.rs'.format(style_struct.name_lower)))[1:-1]}");
- % endfor
- pub const ANIMATABLE_PROPERTY_COUNT: usize = ${sum(1 for prop in data.longhands if prop.animatable)};
-}
-
-macro_rules! unwrap_or_initial {
- ($prop: ident) => (unwrap_or_initial!($prop, $prop));
- ($prop: ident, $expr: expr) =>
- ($expr.unwrap_or_else(|| $prop::get_initial_specified_value()));
-}
-
-/// A module with code for all the shorthand css properties, and a few
-/// serialization helpers.
-#[allow(missing_docs)]
-pub mod shorthands {
- use cssparser::Parser;
- use crate::parser::{Parse, ParserContext};
- use style_traits::{ParseError, StyleParseErrorKind};
- use crate::values::specified;
-
- % for style_struct in data.style_structs:
- include!("${repr(os.path.join(OUT_DIR, 'shorthands/{}.rs'.format(style_struct.name_lower)))[1:-1]}");
- % endfor
-
- // We didn't define the 'all' shorthand using the regular helpers:shorthand
- // mechanism, since it causes some very large types to be generated.
- //
- // Also, make sure logical properties appear before its physical
- // counter-parts, in order to prevent bugs like:
- //
- // https://bugzilla.mozilla.org/show_bug.cgi?id=1410028
- //
- // FIXME(emilio): Adopt the resolution from:
- //
- // https://github.com/w3c/csswg-drafts/issues/1898
- //
- // when there is one, whatever that is.
- <%
- logical_longhands = []
- other_longhands = []
-
- for p in data.longhands:
- if p.name in ['direction', 'unicode-bidi']:
- continue;
- if not p.enabled_in_content() and not p.experimental(engine):
- continue;
- if "Style" not in p.rule_types_allowed_names():
- continue;
- if p.logical:
- logical_longhands.append(p.name)
- else:
- other_longhands.append(p.name)
-
- data.declare_shorthand(
- "all",
- logical_longhands + other_longhands,
- engines="gecko servo",
- spec="https://drafts.csswg.org/css-cascade-3/#all-shorthand"
- )
- %>
-
- /// The max amount of longhands that the `all` shorthand will ever contain.
- pub const ALL_SHORTHAND_MAX_LEN: usize = ${len(logical_longhands + other_longhands)};
-}
-
-<%
- from itertools import groupby
-
- # After this code, `data.longhands` is sorted in the following order:
- # - first all keyword variants and all variants known to be Copy,
- # - second all the other variants, such as all variants with the same field
- # have consecutive discriminants.
- # The variable `variants` contain the same entries as `data.longhands` in
- # the same order, but must exist separately to the data source, because
- # we then need to add three additional variants `WideKeywordDeclaration`,
- # `VariableDeclaration` and `CustomDeclaration`.
-
- variants = []
- for property in data.longhands:
- variants.append({
- "name": property.camel_case,
- "type": property.specified_type(),
- "doc": "`" + property.name + "`",
- "copy": property.specified_is_copy(),
- })
-
- groups = {}
- keyfunc = lambda x: x["type"]
- sortkeys = {}
- for ty, group in groupby(sorted(variants, key=keyfunc), keyfunc):
- group = list(group)
- groups[ty] = group
- for v in group:
- if len(group) == 1:
- sortkeys[v["name"]] = (not v["copy"], 1, v["name"], "")
- else:
- sortkeys[v["name"]] = (not v["copy"], len(group), ty, v["name"])
- variants.sort(key=lambda x: sortkeys[x["name"]])
-
- # It is extremely important to sort the `data.longhands` array here so
- # that it is in the same order as `variants`, for `LonghandId` and
- # `PropertyDeclarationId` to coincide.
- data.longhands.sort(key=lambda x: sortkeys[x.camel_case])
-%>
-
-// WARNING: It is *really* important for the variants of `LonghandId`
-// and `PropertyDeclaration` to be defined in the exact same order,
-// with the exception of `CSSWideKeyword`, `WithVariables` and `Custom`,
-// which don't exist in `LonghandId`.
-
-<%
- extra_variants = [
- {
- "name": "CSSWideKeyword",
- "type": "WideKeywordDeclaration",
- "doc": "A CSS-wide keyword.",
- "copy": False,
- },
- {
- "name": "WithVariables",
- "type": "VariableDeclaration",
- "doc": "An unparsed declaration.",
- "copy": False,
- },
- {
- "name": "Custom",
- "type": "CustomDeclaration",
- "doc": "A custom property declaration.",
- "copy": False,
- },
- ]
- for v in extra_variants:
- variants.append(v)
- groups[v["type"]] = [v]
-%>
-
-/// Servo's representation for a property declaration.
-#[derive(ToShmem)]
-#[repr(u16)]
-pub enum PropertyDeclaration {
- % for variant in variants:
- /// ${variant["doc"]}
- ${variant["name"]}(${variant["type"]}),
- % endfor
-}
-
-// There's one of these for each parsed declaration so it better be small.
-size_of_test!(PropertyDeclaration, 32);
-
-#[repr(C)]
-struct PropertyDeclarationVariantRepr<T> {
- tag: u16,
- value: T
-}
-
-impl Clone for PropertyDeclaration {
- #[inline]
- fn clone(&self) -> Self {
- use self::PropertyDeclaration::*;
-
- <%
- [copy, others] = [list(g) for _, g in groupby(variants, key=lambda x: not x["copy"])]
- %>
-
- let self_tag = unsafe {
- (*(self as *const _ as *const PropertyDeclarationVariantRepr<()>)).tag
- };
- if self_tag <= LonghandId::${copy[-1]["name"]} as u16 {
- #[derive(Clone, Copy)]
- #[repr(u16)]
- enum CopyVariants {
- % for v in copy:
- _${v["name"]}(${v["type"]}),
- % endfor
- }
-
- unsafe {
- let mut out = mem::MaybeUninit::uninit();
- ptr::write(
- out.as_mut_ptr() as *mut CopyVariants,
- *(self as *const _ as *const CopyVariants),
- );
- return out.assume_init();
- }
- }
-
- // This function ensures that all properties not handled above
- // do not have a specified value implements Copy. If you hit
- // compile error here, you may want to add the type name into
- // Longhand.specified_is_copy in data.py.
- fn _static_assert_others_are_not_copy() {
- struct Helper<T>(T);
- trait AssertCopy { fn assert() {} }
- trait AssertNotCopy { fn assert() {} }
- impl<T: Copy> AssertCopy for Helper<T> {}
- % for ty in sorted(set(x["type"] for x in others)):
- impl AssertNotCopy for Helper<${ty}> {}
- Helper::<${ty}>::assert();
- % endfor
- }
-
- match *self {
- ${" |\n".join("{}(..)".format(v["name"]) for v in copy)} => {
- unsafe { debug_unreachable!() }
- }
- % for ty, vs in groupby(others, key=lambda x: x["type"]):
- <%
- vs = list(vs)
- %>
- % if len(vs) == 1:
- ${vs[0]["name"]}(ref value) => {
- ${vs[0]["name"]}(value.clone())
- }
- % else:
- ${" |\n".join("{}(ref value)".format(v["name"]) for v in vs)} => {
- unsafe {
- let mut out = mem::MaybeUninit::uninit();
- ptr::write(
- out.as_mut_ptr() as *mut PropertyDeclarationVariantRepr<${ty}>,
- PropertyDeclarationVariantRepr {
- tag: *(self as *const _ as *const u16),
- value: value.clone(),
- },
- );
- out.assume_init()
- }
- }
- % endif
- % endfor
- }
- }
-}
-
-impl PartialEq for PropertyDeclaration {
- #[inline]
- fn eq(&self, other: &Self) -> bool {
- use self::PropertyDeclaration::*;
-
- unsafe {
- let this_repr =
- &*(self as *const _ as *const PropertyDeclarationVariantRepr<()>);
- let other_repr =
- &*(other as *const _ as *const PropertyDeclarationVariantRepr<()>);
- if this_repr.tag != other_repr.tag {
- return false;
- }
- match *self {
- % for ty, vs in groupby(variants, key=lambda x: x["type"]):
- ${" |\n".join("{}(ref this)".format(v["name"]) for v in vs)} => {
- let other_repr =
- &*(other as *const _ as *const PropertyDeclarationVariantRepr<${ty}>);
- *this == other_repr.value
- }
- % endfor
- }
- }
- }
-}
-
-impl MallocSizeOf for PropertyDeclaration {
- #[inline]
- fn size_of(&self, ops: &mut MallocSizeOfOps) -> usize {
- use self::PropertyDeclaration::*;
-
- match *self {
- % for ty, vs in groupby(variants, key=lambda x: x["type"]):
- ${" | ".join("{}(ref value)".format(v["name"]) for v in vs)} => {
- value.size_of(ops)
- }
- % endfor
- }
- }
-}
-
-
-impl PropertyDeclaration {
- /// Returns whether this is a variant of the Longhand(Value) type, rather
- /// than one of the special variants in extra_variants.
- fn is_longhand_value(&self) -> bool {
- match *self {
- % for v in extra_variants:
- PropertyDeclaration::${v["name"]}(..) => false,
- % endfor
- _ => true,
- }
- }
-
- /// Like the method on ToCss, but without the type parameter to avoid
- /// accidentally monomorphizing this large function multiple times for
- /// different writers.
- pub fn to_css(&self, dest: &mut CssStringWriter) -> fmt::Result {
- use self::PropertyDeclaration::*;
-
- let mut dest = CssWriter::new(dest);
- match *self {
- % for ty, vs in groupby(variants, key=lambda x: x["type"]):
- ${" | ".join("{}(ref value)".format(v["name"]) for v in vs)} => {
- value.to_css(&mut dest)
- }
- % endfor
- }
- }
-
- /// Returns the color value of a given property, for high-contrast-mode
- /// tweaks.
- pub(super) fn color_value(&self) -> Option<<&crate::values::specified::Color> {
- ${static_longhand_id_set("COLOR_PROPERTIES", lambda p: p.predefined_type == "Color")}
- <%
- # sanity check
- assert data.longhands_by_name["background-color"].predefined_type == "Color"
-
- color_specified_type = data.longhands_by_name["background-color"].specified_type()
- %>
- let id = self.id().as_longhand()?;
- if !COLOR_PROPERTIES.contains(id) || !self.is_longhand_value() {
- return None;
- }
- let repr = self as *const _ as *const PropertyDeclarationVariantRepr<${color_specified_type}>;
- Some(unsafe { &(*repr).value })
- }
-}
-
-/// A module with all the code related to animated properties.
-///
-/// This needs to be "included" by mako at least after all longhand modules,
-/// given they populate the global data.
-pub mod animated_properties {
- <%include file="/helpers/animated_properties.mako.rs" />
-}
-
-/// A longhand or shorthand property.
-#[derive(Clone, Copy, Debug)]
-pub struct NonCustomPropertyId(usize);
-
-/// The length of all the non-custom properties.
-pub const NON_CUSTOM_PROPERTY_ID_COUNT: usize =
- ${len(data.longhands) + len(data.shorthands) + len(data.all_aliases())};
-
-/// The length of all counted unknown properties.
-pub const COUNTED_UNKNOWN_PROPERTY_COUNT: usize = ${len(data.counted_unknown_properties)};
-
-% if engine == "gecko":
-#[allow(dead_code)]
-unsafe fn static_assert_nscsspropertyid() {
- % for i, property in enumerate(data.longhands + data.shorthands + data.all_aliases()):
- std::mem::transmute::<[u8; ${i}], [u8; ${property.nscsspropertyid()} as usize]>([0; ${i}]); // ${property.name}
- % endfor
-}
-% endif
-
-impl NonCustomPropertyId {
- /// Returns the underlying index, used for use counter.
- pub fn bit(self) -> usize {
- self.0
- }
-
- /// Convert a `NonCustomPropertyId` into a `nsCSSPropertyID`.
- #[cfg(feature = "gecko")]
- #[inline]
- pub fn to_nscsspropertyid(self) -> nsCSSPropertyID {
- // unsafe: guaranteed by static_assert_nscsspropertyid above.
- unsafe { std::mem::transmute(self.0 as i32) }
- }
-
- /// Convert an `nsCSSPropertyID` into a `NonCustomPropertyId`.
- #[cfg(feature = "gecko")]
- #[inline]
- pub fn from_nscsspropertyid(prop: nsCSSPropertyID) -> Result<Self, ()> {
- let prop = prop as i32;
- if prop < 0 {
- return Err(());
- }
- if prop >= NON_CUSTOM_PROPERTY_ID_COUNT as i32 {
- return Err(());
- }
- // unsafe: guaranteed by static_assert_nscsspropertyid above.
- Ok(unsafe { std::mem::transmute(prop as usize) })
- }
-
- /// Get the property name.
- #[inline]
- pub fn name(self) -> &'static str {
- static MAP: [&'static str; NON_CUSTOM_PROPERTY_ID_COUNT] = [
- % for property in data.longhands + data.shorthands + data.all_aliases():
- "${property.name}",
- % endfor
- ];
- MAP[self.0]
- }
-
- /// Returns whether this property is transitionable.
- #[inline]
- pub fn is_transitionable(self) -> bool {
- ${static_non_custom_property_id_set("TRANSITIONABLE", lambda p: p.transitionable)}
- TRANSITIONABLE.contains(self)
- }
-
- /// Returns whether this property is animatable.
- #[inline]
- pub fn is_animatable(self) -> bool {
- ${static_non_custom_property_id_set("ANIMATABLE", lambda p: p.animatable)}
- ANIMATABLE.contains(self)
- }
-
- #[inline]
- fn enabled_for_all_content(self) -> bool {
- ${static_non_custom_property_id_set(
- "EXPERIMENTAL",
- lambda p: p.experimental(engine)
- )}
-
- ${static_non_custom_property_id_set(
- "ALWAYS_ENABLED",
- lambda p: (not p.experimental(engine)) and p.enabled_in_content()
- )}
-
- let passes_pref_check = || {
- % if engine == "gecko":
- unsafe { structs::nsCSSProps_gPropertyEnabled[self.0] }
- % else:
- static PREF_NAME: [Option< &str>; ${
- len(data.longhands) + len(data.shorthands) + len(data.all_aliases())
- }] = [
- % for property in data.longhands + data.shorthands + data.all_aliases():
- <%
- pref = getattr(property, "servo_pref")
- %>
- % if pref:
- Some("${pref}"),
- % else:
- None,
- % endif
- % endfor
- ];
- let pref = match PREF_NAME[self.0] {
- None => return true,
- Some(pref) => pref,
- };
-
- style_config::get_bool(pref)
- % endif
- };
-
- if ALWAYS_ENABLED.contains(self) {
- return true
- }
-
- if EXPERIMENTAL.contains(self) && passes_pref_check() {
- return true
- }
-
- false
- }
-
- /// Returns whether a given rule allows a given property.
- #[inline]
- pub fn allowed_in_rule(self, rule_types: CssRuleTypes) -> bool {
- debug_assert!(
- rule_types.contains(CssRuleType::Keyframe) ||
- rule_types.contains(CssRuleType::Page) ||
- rule_types.contains(CssRuleType::Style),
- "Declarations are only expected inside a keyframe, page, or style rule."
- );
-
- static MAP: [u32; NON_CUSTOM_PROPERTY_ID_COUNT] = [
- % for property in data.longhands + data.shorthands + data.all_aliases():
- % for name in RULE_VALUES:
- % if property.rule_types_allowed & RULE_VALUES[name] != 0:
- CssRuleType::${name}.bit() |
- % endif
- % endfor
- 0,
- % endfor
- ];
- MAP[self.0] & rule_types.bits() != 0
- }
-
- fn allowed_in(self, context: &ParserContext) -> bool {
- if !self.allowed_in_rule(context.rule_types()) {
- return false;
- }
-
- self.allowed_in_ignoring_rule_type(context)
- }
-
-
- fn allowed_in_ignoring_rule_type(self, context: &ParserContext) -> bool {
- // The semantics of these are kinda hard to reason about, what follows
- // is a description of the different combinations that can happen with
- // these three sets.
- //
- // Experimental properties are generally controlled by prefs, but an
- // experimental property explicitly enabled in certain context (UA or
- // chrome sheets) is always usable in the context regardless of the
- // pref value.
- //
- // Non-experimental properties are either normal properties which are
- // usable everywhere, or internal-only properties which are only usable
- // in certain context they are explicitly enabled in.
- if self.enabled_for_all_content() {
- return true;
- }
-
- ${static_non_custom_property_id_set(
- "ENABLED_IN_UA_SHEETS",
- lambda p: p.explicitly_enabled_in_ua_sheets()
- )}
- ${static_non_custom_property_id_set(
- "ENABLED_IN_CHROME",
- lambda p: p.explicitly_enabled_in_chrome()
- )}
-
- if context.stylesheet_origin == Origin::UserAgent &&
- ENABLED_IN_UA_SHEETS.contains(self)
- {
- return true
- }
-
- if context.chrome_rules_enabled() && ENABLED_IN_CHROME.contains(self) {
- return true
- }
-
- false
- }
-
- /// The supported types of this property. The return value should be
- /// style_traits::CssType when it can become a bitflags type.
- fn supported_types(&self) -> u8 {
- const SUPPORTED_TYPES: [u8; ${len(data.longhands) + len(data.shorthands)}] = [
- % for prop in data.longhands:
- <${prop.specified_type()} as SpecifiedValueInfo>::SUPPORTED_TYPES,
- % endfor
- % for prop in data.shorthands:
- % if prop.name == "all":
- 0, // 'all' accepts no value other than CSS-wide keywords
- % else:
- <shorthands::${prop.ident}::Longhands as SpecifiedValueInfo>::SUPPORTED_TYPES,
- % endif
- % endfor
- ];
- SUPPORTED_TYPES[self.0]
- }
-
- /// See PropertyId::collect_property_completion_keywords.
- fn collect_property_completion_keywords(&self, f: KeywordsCollectFn) {
- fn do_nothing(_: KeywordsCollectFn) {}
- const COLLECT_FUNCTIONS: [fn(KeywordsCollectFn);
- ${len(data.longhands) + len(data.shorthands)}] = [
- % for prop in data.longhands:
- <${prop.specified_type()} as SpecifiedValueInfo>::collect_completion_keywords,
- % endfor
- % for prop in data.shorthands:
- % if prop.name == "all":
- do_nothing, // 'all' accepts no value other than CSS-wide keywords
- % else:
- <shorthands::${prop.ident}::Longhands as SpecifiedValueInfo>::
- collect_completion_keywords,
- % endif
- % endfor
- ];
- COLLECT_FUNCTIONS[self.0](f);
- }
-
- /// Turns this `NonCustomPropertyId` into a `PropertyId`.
- #[inline]
- pub fn to_property_id(self) -> PropertyId {
- use std::mem::transmute;
- if self.0 < ${len(data.longhands)} {
- return unsafe {
- PropertyId::Longhand(transmute(self.0 as u16))
- }
- }
- if self.0 < ${len(data.longhands) + len(data.shorthands)} {
- return unsafe {
- PropertyId::Shorthand(transmute((self.0 - ${len(data.longhands)}) as u16))
- }
- }
- assert!(self.0 < NON_CUSTOM_PROPERTY_ID_COUNT);
- let alias_id: AliasId = unsafe {
- transmute((self.0 - ${len(data.longhands) + len(data.shorthands)}) as u16)
- };
-
- match alias_id.aliased_property() {
- AliasedPropertyId::Longhand(longhand) => PropertyId::LonghandAlias(longhand, alias_id),
- AliasedPropertyId::Shorthand(shorthand) => PropertyId::ShorthandAlias(shorthand, alias_id),
- }
- }
-}
-
-impl From<LonghandId> for NonCustomPropertyId {
- #[inline]
- fn from(id: LonghandId) -> Self {
- NonCustomPropertyId(id as usize)
- }
-}
-
-impl From<ShorthandId> for NonCustomPropertyId {
- #[inline]
- fn from(id: ShorthandId) -> Self {
- NonCustomPropertyId((id as usize) + ${len(data.longhands)})
- }
-}
-
-impl From<AliasId> for NonCustomPropertyId {
- #[inline]
- fn from(id: AliasId) -> Self {
- NonCustomPropertyId(id as usize + ${len(data.longhands) + len(data.shorthands)})
- }
-}
-
-/// A set of all properties
-#[derive(Clone, PartialEq, Default)]
-pub struct NonCustomPropertyIdSet {
- storage: [u32; (NON_CUSTOM_PROPERTY_ID_COUNT - 1 + 32) / 32]
-}
-
-impl NonCustomPropertyIdSet {
- /// Creates an empty `NonCustomPropertyIdSet`.
- pub fn new() -> Self {
- Self {
- storage: Default::default(),
- }
- }
-
- /// Insert a non-custom-property in the set.
- #[inline]
- pub fn insert(&mut self, id: NonCustomPropertyId) {
- let bit = id.0;
- self.storage[bit / 32] |= 1 << (bit % 32);
- }
-
- /// Return whether the given property is in the set
- #[inline]
- pub fn contains(&self, id: NonCustomPropertyId) -> bool {
- let bit = id.0;
- (self.storage[bit / 32] & (1 << (bit % 32))) != 0
- }
-}
-
-<%def name="static_non_custom_property_id_set(name, is_member)">
-static ${name}: NonCustomPropertyIdSet = NonCustomPropertyIdSet {
- <%
- storage = [0] * int((len(data.longhands) + len(data.shorthands) + len(data.all_aliases()) - 1 + 32) / 32)
- for i, property in enumerate(data.longhands + data.shorthands + data.all_aliases()):
- if is_member(property):
- storage[int(i / 32)] |= 1 << (i % 32)
- %>
- storage: [${", ".join("0x%x" % word for word in storage)}]
-};
-</%def>
-
-<%def name="static_longhand_id_set(name, is_member)">
-static ${name}: LonghandIdSet = LonghandIdSet {
- <%
- storage = [0] * int((len(data.longhands) - 1 + 32) / 32)
- for i, property in enumerate(data.longhands):
- if is_member(property):
- storage[int(i / 32)] |= 1 << (i % 32)
- %>
- storage: [${", ".join("0x%x" % word for word in storage)}]
-};
-</%def>
-
-<%
- logical_groups = defaultdict(list)
- for prop in data.longhands:
- if prop.logical_group:
- logical_groups[prop.logical_group].append(prop)
-
- for group, props in logical_groups.items():
- logical_count = sum(1 for p in props if p.logical)
- if logical_count * 2 != len(props):
- raise RuntimeError("Logical group {} has ".format(group) +
- "unbalanced logical / physical properties")
-
- FIRST_LINE_RESTRICTIONS = PropertyRestrictions.first_line(data)
- FIRST_LETTER_RESTRICTIONS = PropertyRestrictions.first_letter(data)
- MARKER_RESTRICTIONS = PropertyRestrictions.marker(data)
- PLACEHOLDER_RESTRICTIONS = PropertyRestrictions.placeholder(data)
- CUE_RESTRICTIONS = PropertyRestrictions.cue(data)
-
- def restriction_flags(property):
- name = property.name
- flags = []
- if name in FIRST_LINE_RESTRICTIONS:
- flags.append("APPLIES_TO_FIRST_LINE")
- if name in FIRST_LETTER_RESTRICTIONS:
- flags.append("APPLIES_TO_FIRST_LETTER")
- if name in PLACEHOLDER_RESTRICTIONS:
- flags.append("APPLIES_TO_PLACEHOLDER")
- if name in MARKER_RESTRICTIONS:
- flags.append("APPLIES_TO_MARKER")
- if name in CUE_RESTRICTIONS:
- flags.append("APPLIES_TO_CUE")
- return flags
-
-%>
-
-/// A group for properties which may override each other
-/// via logical resolution.
-#[derive(Clone, Copy, Eq, Hash, PartialEq)]
-#[repr(u8)]
-pub enum LogicalGroup {
- % for i, group in enumerate(logical_groups.keys()):
- /// ${group}
- ${to_camel_case(group)} = ${i},
- % endfor
-}
-
-
-/// A set of logical groups.
-#[derive(Clone, Copy, Debug, Default, MallocSizeOf, PartialEq)]
-pub struct LogicalGroupSet {
- storage: [u32; (${len(logical_groups)} - 1 + 32) / 32]
-}
-
-impl LogicalGroupSet {
- /// Creates an empty `NonCustomPropertyIdSet`.
- pub fn new() -> Self {
- Self {
- storage: Default::default(),
- }
- }
-
- /// Return whether the given group is in the set
- #[inline]
- pub fn contains(&self, g: LogicalGroup) -> bool {
- let bit = g as usize;
- (self.storage[bit / 32] & (1 << (bit % 32))) != 0
- }
-
- /// Insert a group the set.
- #[inline]
- pub fn insert(&mut self, g: LogicalGroup) {
- let bit = g as usize;
- self.storage[bit / 32] |= 1 << (bit % 32);
- }
-}
-
-/// A set of longhand properties
-#[derive(Clone, Copy, Debug, Default, MallocSizeOf, PartialEq)]
-pub struct LonghandIdSet {
- storage: [u32; (${len(data.longhands)} - 1 + 32) / 32]
-}
-
-impl_trivial_to_shmem!(LonghandIdSet);
-
-/// An iterator over a set of longhand ids.
-pub struct LonghandIdSetIterator<'a> {
- longhands: &'a LonghandIdSet,
- cur: usize,
-}
-
-impl<'a> Iterator for LonghandIdSetIterator<'a> {
- type Item = LonghandId;
-
- fn next(&mut self) -> Option<Self::Item> {
- use std::mem;
-
- loop {
- if self.cur >= ${len(data.longhands)} {
- return None;
- }
-
- let id: LonghandId = unsafe { mem::transmute(self.cur as u16) };
- self.cur += 1;
-
- if self.longhands.contains(id) {
- return Some(id);
- }
- }
- }
-}
-
-<%
-
-CASCADE_GROUPS = {
- # The writing-mode group has the most priority of all property groups, as
- # sizes like font-size can depend on it.
- "writing_mode": [
- "writing-mode",
- "direction",
- "text-orientation",
- ],
- # The fonts and colors group has the second priority, as all other lengths
- # and colors depend on them.
- #
- # There are some interdependencies between these, but we fix them up in
- # Cascade::fixup_font_stuff.
- "fonts_and_color": [
- # Needed to properly compute the zoomed font-size.
- "-x-text-scale",
- # Needed to do font-size computation in a language-dependent way.
- "-x-lang",
- # Needed for ruby to respect language-dependent min-font-size
- # preferences properly, see bug 1165538.
- "-moz-min-font-size-ratio",
- # font-size depends on math-depth's computed value.
- "math-depth",
- # Needed to compute the first available font and its used size,
- # in order to compute font-relative units correctly.
- "font-size",
- "font-size-adjust",
- "font-weight",
- "font-stretch",
- "font-style",
- "font-family",
- # color-scheme affects how system colors resolve.
- "color-scheme",
- "forced-color-adjust",
- ],
-}
-def in_late_group(p):
- return p.name not in CASCADE_GROUPS["writing_mode"] and p.name not in CASCADE_GROUPS["fonts_and_color"]
-
-def is_visited_dependent(p):
- return p.name in [
- "column-rule-color",
- "text-emphasis-color",
- "-webkit-text-fill-color",
- "-webkit-text-stroke-color",
- "text-decoration-color",
- "fill",
- "stroke",
- "caret-color",
- "background-color",
- "border-top-color",
- "border-right-color",
- "border-bottom-color",
- "border-left-color",
- "border-block-start-color",
- "border-inline-end-color",
- "border-block-end-color",
- "border-inline-start-color",
- "outline-color",
- "color",
- ]
-
-%>
-
-impl LonghandIdSet {
- #[inline]
- fn reset() -> &'static Self {
- ${static_longhand_id_set("RESET", lambda p: not p.style_struct.inherited)}
- &RESET
- }
-
- #[inline]
- fn animatable() -> &'static Self {
- ${static_longhand_id_set("ANIMATABLE", lambda p: p.animatable)}
- &ANIMATABLE
- }
-
- #[inline]
- fn discrete_animatable() -> &'static Self {
- ${static_longhand_id_set("DISCRETE_ANIMATABLE", lambda p: p.animation_value_type == "discrete")}
- &DISCRETE_ANIMATABLE
- }
-
- #[inline]
- fn transitionable() -> &'static Self {
- ${static_longhand_id_set("TRANSITIONABLE", lambda p: p.transitionable)}
- &TRANSITIONABLE
- }
-
- #[inline]
- fn logical() -> &'static Self {
- ${static_longhand_id_set("LOGICAL", lambda p: p.logical)}
- &LOGICAL
- }
-
- /// Returns the set of longhands that are ignored when document colors are
- /// disabled.
- #[inline]
- fn ignored_when_colors_disabled() -> &'static Self {
- ${static_longhand_id_set(
- "IGNORED_WHEN_COLORS_DISABLED",
- lambda p: p.ignored_when_colors_disabled
- )}
- &IGNORED_WHEN_COLORS_DISABLED
- }
-
- /// Only a few properties are allowed to depend on the visited state of
- /// links. When cascading visited styles, we can save time by only
- /// processing these properties.
- pub(super) fn visited_dependent() -> &'static Self {
- ${static_longhand_id_set(
- "VISITED_DEPENDENT",
- lambda p: is_visited_dependent(p)
- )}
- debug_assert!(Self::late_group().contains_all(&VISITED_DEPENDENT));
- &VISITED_DEPENDENT
- }
-
- #[inline]
- pub(super) fn writing_mode_group() -> &'static Self {
- ${static_longhand_id_set(
- "WRITING_MODE_GROUP",
- lambda p: p.name in CASCADE_GROUPS["writing_mode"]
- )}
- &WRITING_MODE_GROUP
- }
-
- #[inline]
- pub(super) fn fonts_and_color_group() -> &'static Self {
- ${static_longhand_id_set(
- "FONTS_AND_COLOR_GROUP",
- lambda p: p.name in CASCADE_GROUPS["fonts_and_color"]
- )}
- &FONTS_AND_COLOR_GROUP
- }
-
- #[inline]
- pub(super) fn late_group_only_inherited() -> &'static Self {
- ${static_longhand_id_set("LATE_GROUP_ONLY_INHERITED", lambda p: p.style_struct.inherited and in_late_group(p))}
- &LATE_GROUP_ONLY_INHERITED
- }
-
- #[inline]
- pub(super) fn late_group() -> &'static Self {
- ${static_longhand_id_set("LATE_GROUP", lambda p: in_late_group(p))}
- &LATE_GROUP
- }
-
- /// Returns the set of properties that are declared as having no effect on
- /// Gecko <scrollbar> elements or their descendant scrollbar parts.
- #[cfg(debug_assertions)]
- #[cfg(feature = "gecko")]
- #[inline]
- pub fn has_no_effect_on_gecko_scrollbars() -> &'static Self {
- // data.py asserts that has_no_effect_on_gecko_scrollbars is True or
- // False for properties that are inherited and Gecko pref controlled,
- // and is None for all other properties.
- ${static_longhand_id_set(
- "HAS_NO_EFFECT_ON_SCROLLBARS",
- lambda p: p.has_effect_on_gecko_scrollbars is False
- )}
- &HAS_NO_EFFECT_ON_SCROLLBARS
- }
-
- /// Returns the set of border properties for the purpose of disabling native
- /// appearance.
- #[inline]
- pub fn border_background_properties() -> &'static Self {
- ${static_longhand_id_set(
- "BORDER_BACKGROUND_PROPERTIES",
- lambda p: (p.logical_group and p.logical_group.startswith("border")) or \
- p.name in ["background-color", "background-image"]
- )}
- &BORDER_BACKGROUND_PROPERTIES
- }
-
- /// Iterate over the current longhand id set.
- pub fn iter(&self) -> LonghandIdSetIterator {
- LonghandIdSetIterator { longhands: self, cur: 0, }
- }
-
- /// Returns whether this set contains at least every longhand that `other`
- /// also contains.
- pub fn contains_all(&self, other: &Self) -> bool {
- for (self_cell, other_cell) in self.storage.iter().zip(other.storage.iter()) {
- if (*self_cell & *other_cell) != *other_cell {
- return false;
- }
- }
- true
- }
-
- /// Returns whether this set contains any longhand that `other` also contains.
- pub fn contains_any(&self, other: &Self) -> bool {
- for (self_cell, other_cell) in self.storage.iter().zip(other.storage.iter()) {
- if (*self_cell & *other_cell) != 0 {
- return true;
- }
- }
- false
- }
-
- /// Remove all the given properties from the set.
- #[inline]
- pub fn remove_all(&mut self, other: &Self) {
- for (self_cell, other_cell) in self.storage.iter_mut().zip(other.storage.iter()) {
- *self_cell &= !*other_cell;
- }
- }
-
- /// Create an empty set
- #[inline]
- pub fn new() -> LonghandIdSet {
- LonghandIdSet { storage: [0; (${len(data.longhands)} - 1 + 32) / 32] }
- }
-
- /// Return whether the given property is in the set
- #[inline]
- pub fn contains(&self, id: LonghandId) -> bool {
- let bit = id as usize;
- (self.storage[bit / 32] & (1 << (bit % 32))) != 0
- }
-
- /// Return whether this set contains any reset longhand.
- #[inline]
- pub fn contains_any_reset(&self) -> bool {
- self.contains_any(Self::reset())
- }
-
- /// Add the given property to the set
- #[inline]
- pub fn insert(&mut self, id: LonghandId) {
- let bit = id as usize;
- self.storage[bit / 32] |= 1 << (bit % 32);
- }
-
- /// Remove the given property from the set
- #[inline]
- pub fn remove(&mut self, id: LonghandId) {
- let bit = id as usize;
- self.storage[bit / 32] &= !(1 << (bit % 32));
- }
-
- /// Clear all bits
- #[inline]
- pub fn clear(&mut self) {
- for cell in &mut self.storage {
- *cell = 0
- }
- }
-
- /// Returns whether the set is empty.
- #[inline]
- pub fn is_empty(&self) -> bool {
- self.storage.iter().all(|c| *c == 0)
- }
-}
-
-/// An enum to represent a CSS Wide keyword.
-#[derive(Clone, Copy, Debug, Eq, MallocSizeOf, PartialEq, SpecifiedValueInfo,
- ToCss, ToShmem)]
-pub enum CSSWideKeyword {
- /// The `initial` keyword.
- Initial,
- /// The `inherit` keyword.
- Inherit,
- /// The `unset` keyword.
- Unset,
- /// The `revert` keyword.
- Revert,
- /// The `revert-layer` keyword.
- RevertLayer,
-}
-
-impl CSSWideKeyword {
- fn to_str(&self) -> &'static str {
- match *self {
- CSSWideKeyword::Initial => "initial",
- CSSWideKeyword::Inherit => "inherit",
- CSSWideKeyword::Unset => "unset",
- CSSWideKeyword::Revert => "revert",
- CSSWideKeyword::RevertLayer => "revert-layer",
- }
- }
-}
-
-impl CSSWideKeyword {
- /// Parses a CSS wide keyword from a CSS identifier.
- pub fn from_ident(ident: &str) -> Result<Self, ()> {
- Ok(match_ignore_ascii_case! { ident,
- "initial" => CSSWideKeyword::Initial,
- "inherit" => CSSWideKeyword::Inherit,
- "unset" => CSSWideKeyword::Unset,
- "revert" => CSSWideKeyword::Revert,
- "revert-layer" => CSSWideKeyword::RevertLayer,
- _ => return Err(()),
- })
- }
-
- fn parse(input: &mut Parser) -> Result<Self, ()> {
- let keyword = {
- let ident = input.expect_ident().map_err(|_| ())?;
- Self::from_ident(ident)?
- };
- input.expect_exhausted().map_err(|_| ())?;
- Ok(keyword)
- }
-}
-
-bitflags! {
- /// A set of flags for properties.
- pub struct PropertyFlags: u16 {
- /// This longhand property applies to ::first-letter.
- const APPLIES_TO_FIRST_LETTER = 1 << 1;
- /// This longhand property applies to ::first-line.
- const APPLIES_TO_FIRST_LINE = 1 << 2;
- /// This longhand property applies to ::placeholder.
- const APPLIES_TO_PLACEHOLDER = 1 << 3;
- /// This longhand property applies to ::cue.
- const APPLIES_TO_CUE = 1 << 4;
- /// This longhand property applies to ::marker.
- const APPLIES_TO_MARKER = 1 << 5;
- /// This property is a legacy shorthand.
- ///
- /// https://drafts.csswg.org/css-cascade/#legacy-shorthand
- const IS_LEGACY_SHORTHAND = 1 << 6;
-
- /* The following flags are currently not used in Rust code, they
- * only need to be listed in corresponding properties so that
- * they can be checked in the C++ side via ServoCSSPropList.h. */
- /// This property can be animated on the compositor.
- const CAN_ANIMATE_ON_COMPOSITOR = 0;
- /// This shorthand property is accessible from getComputedStyle.
- const SHORTHAND_IN_GETCS = 0;
- }
-}
-
-/// An identifier for a given longhand property.
-#[derive(Clone, Copy, Eq, Hash, MallocSizeOf, PartialEq, ToComputedValue, ToResolvedValue, ToShmem)]
-#[repr(u16)]
-pub enum LonghandId {
- % for i, property in enumerate(data.longhands):
- /// ${property.name}
- ${property.camel_case} = ${i},
- % endfor
-}
-
-impl ToCss for LonghandId {
- #[inline]
- fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
- where
- W: Write,
- {
- dest.write_str(self.name())
- }
-}
-
-impl fmt::Debug for LonghandId {
- fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
- formatter.write_str(self.name())
- }
-}
-
-impl LonghandId {
- /// Get the name of this longhand property.
- #[inline]
- pub fn name(&self) -> &'static str {
- NonCustomPropertyId::from(*self).name()
- }
-
- /// Returns whether the longhand property is inherited by default.
- #[inline]
- pub fn inherited(self) -> bool {
- !LonghandIdSet::reset().contains(self)
- }
-
- /// Returns an iterator over all the shorthands that include this longhand.
- pub fn shorthands(&self) -> NonCustomPropertyIterator<ShorthandId> {
- // first generate longhand to shorthands lookup map
- //
- // NOTE(emilio): This currently doesn't exclude the "all" shorthand. It
- // could potentially do so, which would speed up serialization
- // algorithms and what not, I guess.
- <%
- from functools import cmp_to_key
- longhand_to_shorthand_map = {}
- num_sub_properties = {}
- for shorthand in data.shorthands:
- num_sub_properties[shorthand.camel_case] = len(shorthand.sub_properties)
- for sub_property in shorthand.sub_properties:
- if sub_property.ident not in longhand_to_shorthand_map:
- longhand_to_shorthand_map[sub_property.ident] = []
-
- longhand_to_shorthand_map[sub_property.ident].append(shorthand.camel_case)
-
- def cmp(a, b):
- return (a > b) - (a < b)
-
- def preferred_order(x, y):
- # Since we want properties in order from most subproperties to least,
- # reverse the arguments to cmp from the expected order.
- result = cmp(num_sub_properties.get(y, 0), num_sub_properties.get(x, 0))
- if result:
- return result
- # Fall back to lexicographic comparison.
- return cmp(x, y)
-
- # Sort the lists of shorthand properties according to preferred order:
- # https://drafts.csswg.org/cssom/#concept-shorthands-preferred-order
- for shorthand_list in longhand_to_shorthand_map.values():
- shorthand_list.sort(key=cmp_to_key(preferred_order))
- %>
-
- // based on lookup results for each longhand, create result arrays
- % for property in data.longhands:
- static ${property.ident.upper()}: &'static [ShorthandId] = &[
- % for shorthand in longhand_to_shorthand_map.get(property.ident, []):
- ShorthandId::${shorthand},
- % endfor
- ];
- % endfor
-
- NonCustomPropertyIterator {
- filter: NonCustomPropertyId::from(*self).enabled_for_all_content(),
- iter: match *self {
- % for property in data.longhands:
- LonghandId::${property.camel_case} => ${property.ident.upper()},
- % endfor
- }.iter(),
- }
- }
-
- fn parse_value<'i, 't>(
- &self,
- context: &ParserContext,
- input: &mut Parser<'i, 't>,
- ) -> Result<PropertyDeclaration, ParseError<'i>> {
- type ParsePropertyFn = for<'i, 't> fn(
- context: &ParserContext,
- input: &mut Parser<'i, 't>,
- ) -> Result<PropertyDeclaration, ParseError<'i>>;
- static PARSE_PROPERTY: [ParsePropertyFn; ${len(data.longhands)}] = [
- % for property in data.longhands:
- longhands::${property.ident}::parse_declared,
- % endfor
- ];
- (PARSE_PROPERTY[*self as usize])(context, input)
- }
-
- /// Returns whether this property is animatable.
- #[inline]
- pub fn is_animatable(self) -> bool {
- LonghandIdSet::animatable().contains(self)
- }
-
- /// Returns whether this property is animatable in a discrete way.
- #[inline]
- pub fn is_discrete_animatable(self) -> bool {
- LonghandIdSet::discrete_animatable().contains(self)
- }
-
- /// Returns whether this property is transitionable.
- #[inline]
- pub fn is_transitionable(self) -> bool {
- LonghandIdSet::transitionable().contains(self)
- }
-
- /// Converts from a LonghandId to an adequate nsCSSPropertyID.
- #[cfg(feature = "gecko")]
- #[inline]
- pub fn to_nscsspropertyid(self) -> nsCSSPropertyID {
- NonCustomPropertyId::from(self).to_nscsspropertyid()
- }
-
- #[cfg(feature = "gecko")]
- #[allow(non_upper_case_globals)]
- /// Returns a longhand id from Gecko's nsCSSPropertyID.
- pub fn from_nscsspropertyid(id: nsCSSPropertyID) -> Result<Self, ()> {
- match PropertyId::from_nscsspropertyid(id) {
- Ok(PropertyId::Longhand(id)) |
- Ok(PropertyId::LonghandAlias(id, _)) => Ok(id),
- _ => Err(()),
- }
- }
-
- /// Return whether this property is logical.
- #[inline]
- pub fn is_logical(self) -> bool {
- LonghandIdSet::logical().contains(self)
- }
-
- /// If this is a logical property, return the corresponding physical one in
- /// the given writing mode.
- ///
- /// Otherwise, return unchanged.
- #[inline]
- pub fn to_physical(&self, wm: WritingMode) -> Self {
- match *self {
- % for property in data.longhands:
- % if property.logical:
- <% logical_group = property.logical_group %>
- LonghandId::${property.camel_case} => {
- <%helpers:logical_setter_helper name="${property.name}">
- <%def name="inner(physical_ident)">
- <%
- physical_name = physical_ident.replace("_", "-")
- physical_property = data.longhands_by_name[physical_name]
- assert logical_group == physical_property.logical_group
- %>
- LonghandId::${to_camel_case(physical_ident)}
- </%def>
- </%helpers:logical_setter_helper>
- }
- % endif
- % endfor
- _ => *self
- }
- }
-
- /// Return the logical group of this longhand property.
- pub fn logical_group(&self) -> Option<LogicalGroup> {
- const LOGICAL_GROUPS: [Option<LogicalGroup>; ${len(data.longhands)}] = [
- % for prop in data.longhands:
- % if prop.logical_group:
- Some(LogicalGroup::${to_camel_case(prop.logical_group)}),
- % else:
- None,
- % endif
- % endfor
- ];
- LOGICAL_GROUPS[*self as usize]
- }
-
- /// Returns PropertyFlags for given longhand property.
- #[inline(always)]
- pub fn flags(self) -> PropertyFlags {
- // TODO(emilio): This can be simplified further as Rust gains more
- // constant expression support.
- const FLAGS: [u16; ${len(data.longhands)}] = [
- % for property in data.longhands:
- % for flag in property.flags + restriction_flags(property):
- PropertyFlags::${flag}.bits |
- % endfor
- 0,
- % endfor
- ];
- PropertyFlags::from_bits_truncate(FLAGS[self as usize])
- }
-
- /// Returns true if the property is one that is ignored when document
- /// colors are disabled.
- #[inline]
- pub fn ignored_when_document_colors_disabled(self) -> bool {
- LonghandIdSet::ignored_when_colors_disabled().contains(self)
- }
-}
-
-/// An iterator over all the property ids that are enabled for a given
-/// shorthand, if that shorthand is enabled for all content too.
-pub struct NonCustomPropertyIterator<Item: 'static> {
- filter: bool,
- iter: std::slice::Iter<'static, Item>,
-}
-
-impl<Item> Iterator for NonCustomPropertyIterator<Item>
-where
- Item: 'static + Copy + Into<NonCustomPropertyId>,
-{
- type Item = Item;
-
- fn next(&mut self) -> Option<Self::Item> {
- loop {
- let id = *self.iter.next()?;
- if !self.filter || id.into().enabled_for_all_content() {
- return Some(id)
- }
- }
- }
-}
-
-/// An identifier for a given shorthand property.
-#[derive(Clone, Copy, Debug, Eq, Hash, MallocSizeOf, PartialEq, ToComputedValue, ToResolvedValue, ToShmem)]
-#[repr(u16)]
-pub enum ShorthandId {
- % for i, property in enumerate(data.shorthands):
- /// ${property.name}
- ${property.camel_case} = ${i},
- % endfor
-}
-
-impl ToCss for ShorthandId {
- #[inline]
- fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
- where
- W: Write,
- {
- dest.write_str(self.name())
- }
-}
-
-impl ShorthandId {
- /// Get the name for this shorthand property.
- #[inline]
- pub fn name(&self) -> &'static str {
- NonCustomPropertyId::from(*self).name()
- }
-
- /// Converts from a ShorthandId to an adequate nsCSSPropertyID.
- #[cfg(feature = "gecko")]
- #[inline]
- pub fn to_nscsspropertyid(self) -> nsCSSPropertyID {
- NonCustomPropertyId::from(self).to_nscsspropertyid()
- }
-
- /// Converts from a nsCSSPropertyID to a ShorthandId.
- #[cfg(feature = "gecko")]
- #[inline]
- pub fn from_nscsspropertyid(prop: nsCSSPropertyID) -> Result<Self, ()> {
- PropertyId::from_nscsspropertyid(prop)?.as_shorthand().map_err(|_| ())
- }
-
- /// Get the longhand ids that form this shorthand.
- pub fn longhands(&self) -> NonCustomPropertyIterator<LonghandId> {
- % for property in data.shorthands:
- static ${property.ident.upper()}: &'static [LonghandId] = &[
- % for sub in property.sub_properties:
- LonghandId::${sub.camel_case},
- % endfor
- ];
- % endfor
- NonCustomPropertyIterator {
- filter: NonCustomPropertyId::from(*self).enabled_for_all_content(),
- iter: match *self {
- % for property in data.shorthands:
- ShorthandId::${property.camel_case} => ${property.ident.upper()},
- % endfor
- }.iter()
- }
- }
-
- /// Try to serialize the given declarations as this shorthand.
- ///
- /// Returns an error if writing to the stream fails, or if the declarations
- /// do not map to a shorthand.
- pub fn longhands_to_css(
- &self,
- declarations: &[&PropertyDeclaration],
- dest: &mut CssStringWriter,
- ) -> fmt::Result {
- type LonghandsToCssFn = for<'a, 'b> fn(&'a [&'b PropertyDeclaration], &mut CssStringWriter) -> fmt::Result;
- fn all_to_css(_: &[&PropertyDeclaration], _: &mut CssStringWriter) -> fmt::Result {
- // No need to try to serialize the declarations as the 'all'
- // shorthand, since it only accepts CSS-wide keywords (and variable
- // references), which will be handled in
- // get_shorthand_appendable_value.
- Ok(())
- }
-
- static LONGHANDS_TO_CSS: [LonghandsToCssFn; ${len(data.shorthands)}] = [
- % for shorthand in data.shorthands:
- % if shorthand.ident == "all":
- all_to_css,
- % else:
- shorthands::${shorthand.ident}::to_css,
- % endif
- % endfor
- ];
-
- LONGHANDS_TO_CSS[*self as usize](declarations, dest)
- }
-
- /// Finds and returns an appendable value for the given declarations.
- ///
- /// Returns the optional appendable value.
- pub fn get_shorthand_appendable_value<'a, 'b: 'a>(
- self,
- declarations: &'a [&'b PropertyDeclaration],
- ) -> Option<AppendableValue<'a, 'b>> {
- let first_declaration = declarations.get(0)?;
- let rest = || declarations.iter().skip(1);
-
- // https://drafts.csswg.org/css-variables/#variables-in-shorthands
- if let Some(css) = first_declaration.with_variables_from_shorthand(self) {
- if rest().all(|d| d.with_variables_from_shorthand(self) == Some(css)) {
- return Some(AppendableValue::Css(css));
- }
- return None;
- }
-
- // Check whether they are all the same CSS-wide keyword.
- if let Some(keyword) = first_declaration.get_css_wide_keyword() {
- if rest().all(|d| d.get_css_wide_keyword() == Some(keyword)) {
- return Some(AppendableValue::Css(keyword.to_str()))
- }
- return None;
- }
-
- if self == ShorthandId::All {
- // 'all' only supports variables and CSS wide keywords.
- return None;
- }
-
- // Check whether all declarations can be serialized as part of shorthand.
- if declarations.iter().all(|d| d.may_serialize_as_part_of_shorthand()) {
- return Some(AppendableValue::DeclarationsForShorthand(self, declarations));
- }
-
- None
- }
-
- /// Returns PropertyFlags for the given shorthand property.
- #[inline]
- pub fn flags(self) -> PropertyFlags {
- const FLAGS: [u16; ${len(data.shorthands)}] = [
- % for property in data.shorthands:
- % for flag in property.flags:
- PropertyFlags::${flag}.bits |
- % endfor
- 0,
- % endfor
- ];
- PropertyFlags::from_bits_truncate(FLAGS[self as usize])
- }
-
- /// Returns whether this property is a legacy shorthand.
- #[inline]
- pub fn is_legacy_shorthand(self) -> bool {
- self.flags().contains(PropertyFlags::IS_LEGACY_SHORTHAND)
- }
-
- /// Returns the order in which this property appears relative to other
- /// shorthands in idl-name-sorting order.
- #[inline]
- pub fn idl_name_sort_order(self) -> u32 {
- <%
- from data import to_idl_name
- ordered = {}
- sorted_shorthands = sorted(data.shorthands, key=lambda p: to_idl_name(p.ident))
- for order, shorthand in enumerate(sorted_shorthands):
- ordered[shorthand.ident] = order
- %>
- static IDL_NAME_SORT_ORDER: [u32; ${len(data.shorthands)}] = [
- % for property in data.shorthands:
- ${ordered[property.ident]},
- % endfor
- ];
- IDL_NAME_SORT_ORDER[self as usize]
- }
-
- fn parse_into<'i, 't>(
- &self,
- declarations: &mut SourcePropertyDeclaration,
- context: &ParserContext,
- input: &mut Parser<'i, 't>,
- ) -> Result<(), ParseError<'i>> {
- type ParseIntoFn = for<'i, 't> fn(
- declarations: &mut SourcePropertyDeclaration,
- context: &ParserContext,
- input: &mut Parser<'i, 't>,
- ) -> Result<(), ParseError<'i>>;
-
- fn parse_all<'i, 't>(
- _: &mut SourcePropertyDeclaration,
- _: &ParserContext,
- input: &mut Parser<'i, 't>
- ) -> Result<(), ParseError<'i>> {
- // 'all' accepts no value other than CSS-wide keywords
- Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError))
- }
-
-
- static PARSE_INTO: [ParseIntoFn; ${len(data.shorthands)}] = [
- % for shorthand in data.shorthands:
- % if shorthand.ident == "all":
- parse_all,
- % else:
- shorthands::${shorthand.ident}::parse_into,
- % endif
- % endfor
- ];
-
- (PARSE_INTO[*self as usize])(declarations, context, input)
- }
-}
-
-/// An unparsed property value that contains `var()` functions.
-#[derive(Debug, Eq, PartialEq, ToShmem)]
-pub struct UnparsedValue {
- /// The css serialization for this value.
- css: String,
- /// The first token type for this serialization.
- first_token_type: TokenSerializationType,
- /// The url data for resolving url values.
- url_data: UrlExtraData,
- /// The shorthand this came from.
- from_shorthand: Option<ShorthandId>,
-}
-
-impl ToCss for UnparsedValue {
- fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
- where
- W: Write,
- {
- // https://drafts.csswg.org/css-variables/#variables-in-shorthands
- if self.from_shorthand.is_none() {
- dest.write_str(&*self.css)?;
- }
- Ok(())
- }
-}
-
-/// A simple cache for properties that come from a shorthand and have variable
-/// references.
-///
-/// This cache works because of the fact that you can't have competing values
-/// for a given longhand coming from the same shorthand (but note that this is
-/// why the shorthand needs to be part of the cache key).
-pub type ShorthandsWithPropertyReferencesCache =
- FxHashMap<(ShorthandId, LonghandId), PropertyDeclaration>;
-
-impl UnparsedValue {
- pub(super) fn substitute_variables<'cache>(
- &self,
- longhand_id: LonghandId,
- writing_mode: WritingMode,
- custom_properties: Option<<&Arc<crate::custom_properties::CustomPropertiesMap>>,
- quirks_mode: QuirksMode,
- device: &Device,
- shorthand_cache: &'cache mut ShorthandsWithPropertyReferencesCache,
- ) -> Cow<'cache, PropertyDeclaration> {
- let invalid_at_computed_value_time = || {
- let keyword = if longhand_id.inherited() {
- CSSWideKeyword::Inherit
- } else {
- CSSWideKeyword::Initial
- };
- Cow::Owned(PropertyDeclaration::css_wide_keyword(longhand_id, keyword))
- };
-
- if let Some(shorthand_id) = self.from_shorthand {
- let key = (shorthand_id, longhand_id);
- if shorthand_cache.contains_key(&key) {
- // FIXME: This double lookup should be avoidable, but rustc
- // doesn't like that, see:
- //
- // https://github.com/rust-lang/rust/issues/82146
- return Cow::Borrowed(&shorthand_cache[&key]);
- }
- }
-
- let css = match crate::custom_properties::substitute(
- &self.css,
- self.first_token_type,
- custom_properties,
- device,
- ) {
- Ok(css) => css,
- Err(..) => return invalid_at_computed_value_time(),
- };
-
- // As of this writing, only the base URL is used for property
- // values.
- //
- // NOTE(emilio): we intentionally pase `None` as the rule type here.
- // If something starts depending on it, it's probably a bug, since
- // it'd change how values are parsed depending on whether we're in a
- // @keyframes rule or not, for example... So think twice about
- // whether you want to do this!
- //
- // FIXME(emilio): ParsingMode is slightly fishy...
- let context = ParserContext::new(
- Origin::Author,
- &self.url_data,
- None,
- ParsingMode::DEFAULT,
- quirks_mode,
- /* namespaces = */ Default::default(),
- None,
- None,
- );
-
- let mut input = ParserInput::new(&css);
- let mut input = Parser::new(&mut input);
- input.skip_whitespace();
-
- if let Ok(keyword) = input.try_parse(CSSWideKeyword::parse) {
- return Cow::Owned(PropertyDeclaration::css_wide_keyword(longhand_id, keyword));
- }
-
- let shorthand = match self.from_shorthand {
- None => {
- return match input.parse_entirely(|input| longhand_id.parse_value(&context, input)) {
- Ok(decl) => Cow::Owned(decl),
- Err(..) => invalid_at_computed_value_time(),
- }
- },
- Some(shorthand) => shorthand,
- };
-
- let mut decls = SourcePropertyDeclaration::default();
- // parse_into takes care of doing `parse_entirely` for us.
- if shorthand.parse_into(&mut decls, &context, &mut input).is_err() {
- return invalid_at_computed_value_time();
- }
-
- for declaration in decls.declarations.drain(..) {
- let longhand = declaration.id().as_longhand().unwrap();
- if longhand.is_logical() {
- shorthand_cache.insert((shorthand, longhand.to_physical(writing_mode)), declaration.clone());
- }
- shorthand_cache.insert((shorthand, longhand), declaration);
- }
-
- let key = (shorthand, longhand_id);
- match shorthand_cache.get(&key) {
- Some(decl) => Cow::Borrowed(decl),
- None => {
- // FIXME: We should always have the key here but it seems
- // sometimes we don't, see bug 1696409.
- #[cfg(feature = "gecko")]
- {
- if structs::GECKO_IS_NIGHTLY {
- panic!("Expected {:?} to be in the cache but it was not!", key);
- }
- }
- invalid_at_computed_value_time()
- }
- }
- }
-}
-
-/// An identifier for a given property declaration, which can be either a
-/// longhand or a custom property.
-#[derive(Clone, Copy, Debug, PartialEq)]
-#[cfg_attr(feature = "servo", derive(MallocSizeOf))]
-pub enum PropertyDeclarationId<'a> {
- /// A longhand.
- Longhand(LonghandId),
- /// A custom property declaration.
- Custom(&'a crate::custom_properties::Name),
-}
-
-impl<'a> ToCss for PropertyDeclarationId<'a> {
- fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
- where
- W: Write,
- {
- match *self {
- PropertyDeclarationId::Longhand(id) => dest.write_str(id.name()),
- PropertyDeclarationId::Custom(ref name) => {
- dest.write_str("--")?;
- serialize_atom_name(name, dest)
- }
- }
- }
-}
-
-impl<'a> PropertyDeclarationId<'a> {
- /// Whether a given declaration id is either the same as `other`, or a
- /// longhand of it.
- pub fn is_or_is_longhand_of(&self, other: &PropertyId) -> bool {
- match *self {
- PropertyDeclarationId::Longhand(id) => {
- match *other {
- PropertyId::Longhand(other_id) |
- PropertyId::LonghandAlias(other_id, _) => id == other_id,
- PropertyId::Shorthand(shorthand) |
- PropertyId::ShorthandAlias(shorthand, _) => self.is_longhand_of(shorthand),
- PropertyId::Custom(_) => false,
- }
- }
- PropertyDeclarationId::Custom(name) => {
- matches!(*other, PropertyId::Custom(ref other_name) if name == other_name)
- }
- }
- }
-
- /// Whether a given declaration id is a longhand belonging to this
- /// shorthand.
- pub fn is_longhand_of(&self, shorthand: ShorthandId) -> bool {
- match *self {
- PropertyDeclarationId::Longhand(ref id) => id.shorthands().any(|s| s == shorthand),
- _ => false,
- }
- }
-
- /// Returns the name of the property without CSS escaping.
- pub fn name(&self) -> Cow<'static, str> {
- match *self {
- PropertyDeclarationId::Longhand(id) => id.name().into(),
- PropertyDeclarationId::Custom(name) => {
- let mut s = String::new();
- write!(&mut s, "--{}", name).unwrap();
- s.into()
- }
- }
- }
-
- /// Returns longhand id if it is, None otherwise.
- #[inline]
- pub fn as_longhand(&self) -> Option<LonghandId> {
- match *self {
- PropertyDeclarationId::Longhand(id) => Some(id),
- _ => None,
- }
- }
-}
-
-/// Servo's representation of a CSS property, that is, either a longhand, a
-/// shorthand, or a custom property.
-#[derive(Clone, Eq, PartialEq)]
-pub enum PropertyId {
- /// A longhand property.
- Longhand(LonghandId),
- /// A shorthand property.
- Shorthand(ShorthandId),
- /// An alias for a longhand property.
- LonghandAlias(LonghandId, AliasId),
- /// An alias for a shorthand property.
- ShorthandAlias(ShorthandId, AliasId),
- /// A custom property.
- Custom(crate::custom_properties::Name),
-}
-
-impl fmt::Debug for PropertyId {
- fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
- self.to_css(&mut CssWriter::new(formatter))
- }
-}
-
-impl ToCss for PropertyId {
- fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
- where
- W: Write,
- {
- match *self {
- PropertyId::Longhand(id) => dest.write_str(id.name()),
- PropertyId::Shorthand(id) => dest.write_str(id.name()),
- PropertyId::LonghandAlias(id, _) => dest.write_str(id.name()),
- PropertyId::ShorthandAlias(id, _) => dest.write_str(id.name()),
- PropertyId::Custom(ref name) => {
- dest.write_str("--")?;
- serialize_atom_name(name, dest)
- }
- }
- }
-}
-
-/// The counted unknown property list which is used for css use counters.
-///
-/// FIXME: This should be just #[repr(u8)], but can't be because of ABI issues,
-/// see https://bugs.llvm.org/show_bug.cgi?id=44228.
-#[derive(Clone, Copy, Debug, Eq, FromPrimitive, Hash, PartialEq)]
-#[repr(u32)]
-pub enum CountedUnknownProperty {
- % for prop in data.counted_unknown_properties:
- /// ${prop.name}
- ${prop.camel_case},
- % endfor
-}
-
-impl CountedUnknownProperty {
- /// Parse the counted unknown property, for testing purposes only.
- pub fn parse_for_testing(property_name: &str) -> Option<Self> {
- ascii_case_insensitive_phf_map! {
- unknown_id -> CountedUnknownProperty = {
- % for property in data.counted_unknown_properties:
- "${property.name}" => CountedUnknownProperty::${property.camel_case},
- % endfor
- }
- }
- unknown_id(property_name).cloned()
- }
-
- /// Returns the underlying index, used for use counter.
- #[inline]
- pub fn bit(self) -> usize {
- self as usize
- }
-}
-
-impl PropertyId {
- /// Return the longhand id that this property id represents.
- #[inline]
- pub fn longhand_id(&self) -> Option<LonghandId> {
- Some(match *self {
- PropertyId::Longhand(id) => id,
- PropertyId::LonghandAlias(id, _) => id,
- _ => return None,
- })
- }
-
- /// Returns a given property from the given name, _regardless of whether it
- /// is enabled or not_, or Err(()) for unknown properties.
- ///
- /// Do not use for non-testing purposes.
- pub fn parse_unchecked_for_testing(name: &str) -> Result<Self, ()> {
- Self::parse_unchecked(name, None)
- }
-
- /// Returns a given property from the given name, _regardless of whether it
- /// is enabled or not_, or Err(()) for unknown properties.
- fn parse_unchecked(
- property_name: &str,
- use_counters: Option< &UseCounters>,
- ) -> Result<Self, ()> {
- // A special id for css use counters.
- // ShorthandAlias is not used in the Servo build.
- // That's why we need to allow dead_code.
- #[allow(dead_code)]
- pub enum StaticId {
- Longhand(LonghandId),
- Shorthand(ShorthandId),
- LonghandAlias(LonghandId, AliasId),
- ShorthandAlias(ShorthandId, AliasId),
- CountedUnknown(CountedUnknownProperty),
- }
- ascii_case_insensitive_phf_map! {
- static_id -> StaticId = {
- % for (kind, properties) in [("Longhand", data.longhands), ("Shorthand", data.shorthands)]:
- % for property in properties:
- "${property.name}" => StaticId::${kind}(${kind}Id::${property.camel_case}),
- % for alias in property.aliases:
- "${alias.name}" => {
- StaticId::${kind}Alias(
- ${kind}Id::${property.camel_case},
- AliasId::${alias.camel_case},
- )
- },
- % endfor
- % endfor
- % endfor
- % for property in data.counted_unknown_properties:
- "${property.name}" => {
- StaticId::CountedUnknown(CountedUnknownProperty::${property.camel_case})
- },
- % endfor
- }
- }
-
- if let Some(id) = static_id(property_name) {
- return Ok(match *id {
- StaticId::Longhand(id) => PropertyId::Longhand(id),
- StaticId::Shorthand(id) => {
- #[cfg(feature = "gecko")]
- {
- // We want to count `zoom` even if disabled.
- if matches!(id, ShorthandId::Zoom) {
- if let Some(counters) = use_counters {
- counters.non_custom_properties.record(id.into());
- }
- }
- }
-
- PropertyId::Shorthand(id)
- },
- StaticId::LonghandAlias(id, alias) => PropertyId::LonghandAlias(id, alias),
- StaticId::ShorthandAlias(id, alias) => PropertyId::ShorthandAlias(id, alias),
- StaticId::CountedUnknown(unknown_prop) => {
- if let Some(counters) = use_counters {
- counters.counted_unknown_properties.record(unknown_prop);
- }
-
- // Always return Err(()) because these aren't valid custom property names.
- return Err(());
- }
- });
- }
-
- let name = crate::custom_properties::parse_name(property_name)?;
- Ok(PropertyId::Custom(crate::custom_properties::Name::from(name)))
- }
-
- /// Parses a property name, and returns an error if it's unknown or isn't
- /// enabled for all content.
- #[inline]
- pub fn parse_enabled_for_all_content(name: &str) -> Result<Self, ()> {
- let id = Self::parse_unchecked(name, None)?;
-
- if !id.enabled_for_all_content() {
- return Err(());
- }
-
- Ok(id)
- }
-
-
- /// Parses a property name, and returns an error if it's unknown or isn't
- /// allowed in this context.
- #[inline]
- pub fn parse(name: &str, context: &ParserContext) -> Result<Self, ()> {
- let id = Self::parse_unchecked(name, context.use_counters)?;
-
- if !id.allowed_in(context) {
- return Err(());
- }
-
- Ok(id)
- }
-
- /// Parses a property name, and returns an error if it's unknown or isn't
- /// allowed in this context, ignoring the rule_type checks.
- ///
- /// This is useful for parsing stuff from CSS values, for example.
- #[inline]
- pub fn parse_ignoring_rule_type(
- name: &str,
- context: &ParserContext,
- ) -> Result<Self, ()> {
- let id = Self::parse_unchecked(name, None)?;
-
- if !id.allowed_in_ignoring_rule_type(context) {
- return Err(());
- }
-
- Ok(id)
- }
-
- /// Returns a property id from Gecko's nsCSSPropertyID.
- #[cfg(feature = "gecko")]
- #[allow(non_upper_case_globals)]
- #[inline]
- pub fn from_nscsspropertyid(id: nsCSSPropertyID) -> Result<Self, ()> {
- Ok(NonCustomPropertyId::from_nscsspropertyid(id)?.to_property_id())
- }
-
- /// Returns true if the property is a shorthand or shorthand alias.
- #[inline]
- pub fn is_shorthand(&self) -> bool {
- self.as_shorthand().is_ok()
- }
-
- /// Given this property id, get it either as a shorthand or as a
- /// `PropertyDeclarationId`.
- pub fn as_shorthand(&self) -> Result<ShorthandId, PropertyDeclarationId> {
- match *self {
- PropertyId::ShorthandAlias(id, _) |
- PropertyId::Shorthand(id) => Ok(id),
- PropertyId::LonghandAlias(id, _) |
- PropertyId::Longhand(id) => Err(PropertyDeclarationId::Longhand(id)),
- PropertyId::Custom(ref name) => Err(PropertyDeclarationId::Custom(name)),
- }
- }
-
- /// Returns the `NonCustomPropertyId` corresponding to this property id.
- pub fn non_custom_id(&self) -> Option<NonCustomPropertyId> {
- Some(match *self {
- PropertyId::Custom(_) => return None,
- PropertyId::Shorthand(shorthand_id) => shorthand_id.into(),
- PropertyId::Longhand(longhand_id) => longhand_id.into(),
- PropertyId::ShorthandAlias(_, alias_id) => alias_id.into(),
- PropertyId::LonghandAlias(_, alias_id) => alias_id.into(),
- })
- }
-
- /// Returns non-alias NonCustomPropertyId corresponding to this
- /// property id.
- fn non_custom_non_alias_id(&self) -> Option<NonCustomPropertyId> {
- Some(match *self {
- PropertyId::Custom(_) => return None,
- PropertyId::Shorthand(id) => id.into(),
- PropertyId::Longhand(id) => id.into(),
- PropertyId::ShorthandAlias(id, _) => id.into(),
- PropertyId::LonghandAlias(id, _) => id.into(),
- })
- }
-
- /// Whether the property is enabled for all content regardless of the
- /// stylesheet it was declared on (that is, in practice only checks prefs).
- #[inline]
- pub fn enabled_for_all_content(&self) -> bool {
- let id = match self.non_custom_id() {
- // Custom properties are allowed everywhere
- None => return true,
- Some(id) => id,
- };
-
- id.enabled_for_all_content()
- }
-
- /// Converts this PropertyId in nsCSSPropertyID, resolving aliases to the
- /// resolved property, and returning eCSSPropertyExtra_variable for custom
- /// properties.
- #[cfg(feature = "gecko")]
- #[inline]
- pub fn to_nscsspropertyid_resolving_aliases(&self) -> nsCSSPropertyID {
- match self.non_custom_non_alias_id() {
- Some(id) => id.to_nscsspropertyid(),
- None => nsCSSPropertyID::eCSSPropertyExtra_variable,
- }
- }
-
- fn allowed_in(&self, context: &ParserContext) -> bool {
- let id = match self.non_custom_id() {
- // Custom properties are allowed everywhere
- None => return true,
- Some(id) => id,
- };
- id.allowed_in(context)
- }
-
- #[inline]
- fn allowed_in_ignoring_rule_type(&self, context: &ParserContext) -> bool {
- let id = match self.non_custom_id() {
- // Custom properties are allowed everywhere
- None => return true,
- Some(id) => id,
- };
- id.allowed_in_ignoring_rule_type(context)
- }
-
- /// Whether the property supports the given CSS type.
- /// `ty` should a bitflags of constants in style_traits::CssType.
- pub fn supports_type(&self, ty: u8) -> bool {
- let id = self.non_custom_non_alias_id();
- id.map_or(0, |id| id.supported_types()) & ty != 0
- }
-
- /// Collect supported starting word of values of this property.
- ///
- /// See style_traits::SpecifiedValueInfo::collect_completion_keywords for more
- /// details.
- pub fn collect_property_completion_keywords(&self, f: KeywordsCollectFn) {
- if let Some(id) = self.non_custom_non_alias_id() {
- id.collect_property_completion_keywords(f);
- }
- CSSWideKeyword::collect_completion_keywords(f);
- }
-}
-
-/// A declaration using a CSS-wide keyword.
-#[cfg_attr(feature = "gecko", derive(MallocSizeOf))]
-#[derive(Clone, PartialEq, ToCss, ToShmem)]
-pub struct WideKeywordDeclaration {
- #[css(skip)]
- id: LonghandId,
- /// The CSS-wide keyword.
- pub keyword: CSSWideKeyword,
-}
-
-/// An unparsed declaration that contains `var()` functions.
-#[cfg_attr(feature = "gecko", derive(MallocSizeOf))]
-#[derive(Clone, PartialEq, ToCss, ToShmem)]
-pub struct VariableDeclaration {
- /// The id of the property this declaration represents.
- #[css(skip)]
- pub id: LonghandId,
- /// The unparsed value of the variable.
- #[cfg_attr(feature = "gecko", ignore_malloc_size_of = "XXX: how to handle this?")]
- pub value: Arc<UnparsedValue>,
-}
-
-/// A custom property declaration value is either an unparsed value or a CSS
-/// wide-keyword.
-#[derive(Clone, PartialEq, ToCss, ToShmem)]
-pub enum CustomDeclarationValue {
- /// A value.
- Value(Arc<crate::custom_properties::SpecifiedValue>),
- /// A wide keyword.
- CSSWideKeyword(CSSWideKeyword),
-}
-
-/// A custom property declaration with the property name and the declared value.
-#[cfg_attr(feature = "gecko", derive(MallocSizeOf))]
-#[derive(Clone, PartialEq, ToCss, ToShmem)]
-pub struct CustomDeclaration {
- /// The name of the custom property.
- #[css(skip)]
- pub name: crate::custom_properties::Name,
- /// The value of the custom property.
- #[cfg_attr(feature = "gecko", ignore_malloc_size_of = "XXX: how to handle this?")]
- pub value: CustomDeclarationValue,
-}
-
-impl fmt::Debug for PropertyDeclaration {
- fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
- self.id().to_css(&mut CssWriter::new(f))?;
- f.write_str(": ")?;
-
- // Because PropertyDeclaration::to_css requires CssStringWriter, we can't write
- // it directly to f, and need to allocate an intermediate string. This is
- // fine for debug-only code.
- let mut s = CssString::new();
- self.to_css(&mut s)?;
- write!(f, "{}", s)
- }
-}
-
-impl PropertyDeclaration {
- /// Given a property declaration, return the property declaration id.
- #[inline]
- pub fn id(&self) -> PropertyDeclarationId {
- match *self {
- PropertyDeclaration::Custom(ref declaration) => {
- return PropertyDeclarationId::Custom(&declaration.name)
- }
- PropertyDeclaration::CSSWideKeyword(ref declaration) => {
- return PropertyDeclarationId::Longhand(declaration.id);
- }
- PropertyDeclaration::WithVariables(ref declaration) => {
- return PropertyDeclarationId::Longhand(declaration.id);
- }
- _ => {}
- }
- // This is just fine because PropertyDeclaration and LonghandId
- // have corresponding discriminants.
- let id = unsafe { *(self as *const _ as *const LonghandId) };
- debug_assert_eq!(id, match *self {
- % for property in data.longhands:
- PropertyDeclaration::${property.camel_case}(..) => LonghandId::${property.camel_case},
- % endfor
- _ => id,
- });
- PropertyDeclarationId::Longhand(id)
- }
-
- /// Given a declaration, convert it into a declaration for a corresponding
- /// physical property.
- #[inline]
- pub fn to_physical(&self, wm: WritingMode) -> Self {
- match *self {
- PropertyDeclaration::WithVariables(VariableDeclaration {
- id,
- ref value,
- }) => {
- return PropertyDeclaration::WithVariables(VariableDeclaration {
- id: id.to_physical(wm),
- value: value.clone(),
- })
- }
- PropertyDeclaration::CSSWideKeyword(WideKeywordDeclaration {
- id,
- keyword,
- }) => {
- return PropertyDeclaration::CSSWideKeyword(WideKeywordDeclaration {
- id: id.to_physical(wm),
- keyword,
- })
- }
- PropertyDeclaration::Custom(..) => return self.clone(),
- % for prop in data.longhands:
- PropertyDeclaration::${prop.camel_case}(..) => {},
- % endfor
- }
-
- let mut ret = self.clone();
-
- % for prop in data.longhands:
- % for physical_property in prop.all_physical_mapped_properties(data):
- % if physical_property.specified_type() != prop.specified_type():
- <% raise "Logical property %s should share specified value with physical property %s" % \
- (prop.name, physical_property.name) %>
- % endif
- % endfor
- % endfor
-
- unsafe {
- let longhand_id = *(&mut ret as *mut _ as *mut LonghandId);
-
- debug_assert_eq!(
- PropertyDeclarationId::Longhand(longhand_id),
- ret.id()
- );
-
- // This is just fine because PropertyDeclaration and LonghandId
- // have corresponding discriminants.
- *(&mut ret as *mut _ as *mut LonghandId) = longhand_id.to_physical(wm);
-
- debug_assert_eq!(
- PropertyDeclarationId::Longhand(longhand_id.to_physical(wm)),
- ret.id()
- );
- }
-
- ret
- }
-
- fn with_variables_from_shorthand(&self, shorthand: ShorthandId) -> Option< &str> {
- match *self {
- PropertyDeclaration::WithVariables(ref declaration) => {
- let s = declaration.value.from_shorthand?;
- if s != shorthand {
- return None;
- }
- Some(&*declaration.value.css)
- },
- _ => None,
- }
- }
-
- /// Returns a CSS-wide keyword declaration for a given property.
- #[inline]
- pub fn css_wide_keyword(id: LonghandId, keyword: CSSWideKeyword) -> Self {
- Self::CSSWideKeyword(WideKeywordDeclaration { id, keyword })
- }
-
- /// Returns a CSS-wide keyword if the declaration's value is one.
- #[inline]
- pub fn get_css_wide_keyword(&self) -> Option<CSSWideKeyword> {
- match *self {
- PropertyDeclaration::CSSWideKeyword(ref declaration) => {
- Some(declaration.keyword)
- },
- _ => None,
- }
- }
-
- /// Returns whether or not the property is set by a system font
- pub fn get_system(&self) -> Option<SystemFont> {
- match *self {
- % if engine == "gecko":
- % for prop in SYSTEM_FONT_LONGHANDS:
- PropertyDeclaration::${to_camel_case(prop)}(ref prop) => {
- prop.get_system()
- }
- % endfor
- % endif
- _ => None,
- }
- }
-
- /// Is it the default value of line-height?
- pub fn is_default_line_height(&self) -> bool {
- match *self {
- PropertyDeclaration::LineHeight(LineHeight::Normal) => true,
- _ => false
- }
- }
-
- /// Returns whether the declaration may be serialized as part of a shorthand.
- ///
- /// This method returns false if this declaration contains variable or has a
- /// CSS-wide keyword value, since these values cannot be serialized as part
- /// of a shorthand.
- ///
- /// Caller should check `with_variables_from_shorthand()` and whether all
- /// needed declarations has the same CSS-wide keyword first.
- ///
- /// Note that, serialization of a shorthand may still fail because of other
- /// property-specific requirement even when this method returns true for all
- /// the longhand declarations.
- pub fn may_serialize_as_part_of_shorthand(&self) -> bool {
- match *self {
- PropertyDeclaration::CSSWideKeyword(..) |
- PropertyDeclaration::WithVariables(..) => false,
- PropertyDeclaration::Custom(..) =>
- unreachable!("Serializing a custom property as part of shorthand?"),
- _ => true,
- }
- }
-
- /// Return whether the value is stored as it was in the CSS source,
- /// preserving whitespace (as opposed to being parsed into a more abstract
- /// data structure).
- ///
- /// This is the case of custom properties and values that contain
- /// unsubstituted variables.
- pub fn value_is_unparsed(&self) -> bool {
- match *self {
- PropertyDeclaration::WithVariables(..) => true,
- PropertyDeclaration::Custom(ref declaration) => {
- matches!(declaration.value, CustomDeclarationValue::Value(..))
- }
- _ => false,
- }
- }
-
- /// Returns true if this property declaration is for one of the animatable
- /// properties.
- pub fn is_animatable(&self) -> bool {
- match self.id() {
- PropertyDeclarationId::Longhand(id) => id.is_animatable(),
- PropertyDeclarationId::Custom(..) => false,
- }
- }
-
- /// Returns true if this property is a custom property, false
- /// otherwise.
- pub fn is_custom(&self) -> bool {
- matches!(*self, PropertyDeclaration::Custom(..))
- }
-
- /// The `context` parameter controls this:
- ///
- /// <https://drafts.csswg.org/css-animations/#keyframes>
- /// > The <declaration-list> inside of <keyframe-block> accepts any CSS property
- /// > except those defined in this specification,
- /// > but does accept the `animation-play-state` property and interprets it specially.
- ///
- /// This will not actually parse Importance values, and will always set things
- /// to Importance::Normal. Parsing Importance values is the job of PropertyDeclarationParser,
- /// we only set them here so that we don't have to reallocate
- pub fn parse_into<'i, 't>(
- declarations: &mut SourcePropertyDeclaration,
- id: PropertyId,
- context: &ParserContext,
- input: &mut Parser<'i, 't>,
- ) -> Result<(), ParseError<'i>> {
- assert!(declarations.is_empty());
- debug_assert!(id.allowed_in(context), "{:?}", id);
-
- let non_custom_id = id.non_custom_id();
- input.skip_whitespace();
-
- let start = input.state();
- match id {
- PropertyId::Custom(property_name) => {
- let value = match input.try_parse(CSSWideKeyword::parse) {
- Ok(keyword) => CustomDeclarationValue::CSSWideKeyword(keyword),
- Err(()) => CustomDeclarationValue::Value(
- crate::custom_properties::SpecifiedValue::parse(input)?
- ),
- };
- declarations.push(PropertyDeclaration::Custom(CustomDeclaration {
- name: property_name,
- value,
- }));
- return Ok(());
- }
- PropertyId::LonghandAlias(id, _) |
- PropertyId::Longhand(id) => {
- input.try_parse(CSSWideKeyword::parse).map(|keyword| {
- PropertyDeclaration::css_wide_keyword(id, keyword)
- }).or_else(|()| {
- input.look_for_var_or_env_functions();
- input.parse_entirely(|input| id.parse_value(context, input))
- .or_else(|err| {
- while let Ok(_) = input.next() {} // Look for var() after the error.
- if !input.seen_var_or_env_functions() {
- return Err(err);
- }
- input.reset(&start);
- let (first_token_type, css) =
- crate::custom_properties::parse_non_custom_with_var(input)?;
- Ok(PropertyDeclaration::WithVariables(VariableDeclaration {
- id,
- value: Arc::new(UnparsedValue {
- css: css.into_owned(),
- first_token_type,
- url_data: context.url_data.clone(),
- from_shorthand: None,
- }),
- }))
- })
- }).map(|declaration| {
- declarations.push(declaration)
- })?;
- }
- PropertyId::ShorthandAlias(id, _) |
- PropertyId::Shorthand(id) => {
- if let Ok(keyword) = input.try_parse(CSSWideKeyword::parse) {
- if id == ShorthandId::All {
- declarations.all_shorthand = AllShorthand::CSSWideKeyword(keyword)
- } else {
- for longhand in id.longhands() {
- declarations.push(PropertyDeclaration::css_wide_keyword(longhand, keyword));
- }
- }
- } else {
- input.look_for_var_or_env_functions();
- // Not using parse_entirely here: each
- // ${shorthand.ident}::parse_into function needs to do so
- // *before* pushing to `declarations`.
- id.parse_into(declarations, context, input).or_else(|err| {
- while let Ok(_) = input.next() {} // Look for var() after the error.
- if !input.seen_var_or_env_functions() {
- return Err(err);
- }
-
- input.reset(&start);
- let (first_token_type, css) =
- crate::custom_properties::parse_non_custom_with_var(input)?;
- let unparsed = Arc::new(UnparsedValue {
- css: css.into_owned(),
- first_token_type,
- url_data: context.url_data.clone(),
- from_shorthand: Some(id),
- });
- if id == ShorthandId::All {
- declarations.all_shorthand = AllShorthand::WithVariables(unparsed)
- } else {
- for id in id.longhands() {
- declarations.push(
- PropertyDeclaration::WithVariables(VariableDeclaration {
- id,
- value: unparsed.clone(),
- })
- )
- }
- }
- Ok(())
- })?;
- }
- }
- }
- debug_assert!(non_custom_id.is_some(), "Custom properties should've returned earlier");
- if let Some(use_counters) = context.use_counters {
- use_counters.non_custom_properties.record(non_custom_id.unwrap());
- }
- Ok(())
- }
-}
-
-const SUB_PROPERTIES_ARRAY_CAP: usize =
- ${max(len(s.sub_properties) for s in data.shorthands_except_all()) \
- if data.shorthands_except_all() else 0};
-
-/// An ArrayVec of subproperties, contains space for the longest shorthand except all.
-pub type SubpropertiesVec<T> = ArrayVec<T, SUB_PROPERTIES_ARRAY_CAP>;
-
-/// A stack-allocated vector of `PropertyDeclaration`
-/// large enough to parse one CSS `key: value` declaration.
-/// (Shorthands expand to multiple `PropertyDeclaration`s.)
-#[derive(Default)]
-pub struct SourcePropertyDeclaration {
- /// The storage for the actual declarations (except for all).
- pub declarations: SubpropertiesVec<PropertyDeclaration>,
- /// Stored separately to keep SubpropertiesVec smaller.
- pub all_shorthand: AllShorthand,
-}
-
-// This is huge, but we allocate it on the stack and then never move it,
-// we only pass `&mut SourcePropertyDeclaration` references around.
-size_of_test!(SourcePropertyDeclaration, 568);
-
-impl SourcePropertyDeclaration {
- /// Create one with a single PropertyDeclaration.
- #[inline]
- pub fn with_one(decl: PropertyDeclaration) -> Self {
- let mut result = Self::default();
- result.declarations.push(decl);
- result
- }
-
- /// Similar to Vec::drain: leaves this empty when the return value is dropped.
- pub fn drain(&mut self) -> SourcePropertyDeclarationDrain {
- SourcePropertyDeclarationDrain {
- declarations: self.declarations.drain(..),
- all_shorthand: mem::replace(&mut self.all_shorthand, AllShorthand::NotSet),
- }
- }
-
- /// Reset to initial state
- pub fn clear(&mut self) {
- self.declarations.clear();
- self.all_shorthand = AllShorthand::NotSet;
- }
-
- fn is_empty(&self) -> bool {
- self.declarations.is_empty() && matches!(self.all_shorthand, AllShorthand::NotSet)
- }
-
- fn push(&mut self, declaration: PropertyDeclaration) {
- let _result = self.declarations.try_push(declaration);
- debug_assert!(_result.is_ok());
- }
-}
-
-/// Return type of SourcePropertyDeclaration::drain
-pub struct SourcePropertyDeclarationDrain<'a> {
- /// A drain over the non-all declarations.
- pub declarations: ArrayVecDrain<'a, PropertyDeclaration, SUB_PROPERTIES_ARRAY_CAP>,
- /// The all shorthand that was set.
- pub all_shorthand: AllShorthand,
-}
-
-/// A parsed all-shorthand value.
-pub enum AllShorthand {
- /// Not present.
- NotSet,
- /// A CSS-wide keyword.
- CSSWideKeyword(CSSWideKeyword),
- /// An all shorthand with var() references that we can't resolve right now.
- WithVariables(Arc<UnparsedValue>)
-}
-
-impl Default for AllShorthand {
- fn default() -> Self {
- Self::NotSet
- }
-}
-
-impl AllShorthand {
- /// Iterates property declarations from the given all shorthand value.
- #[inline]
- pub fn declarations(&self) -> AllShorthandDeclarationIterator {
- AllShorthandDeclarationIterator {
- all_shorthand: self,
- longhands: ShorthandId::All.longhands(),
- }
- }
-}
-
-/// An iterator over the all shorthand's shorthand declarations.
-pub struct AllShorthandDeclarationIterator<'a> {
- all_shorthand: &'a AllShorthand,
- longhands: NonCustomPropertyIterator<LonghandId>,
-}
-
-impl<'a> Iterator for AllShorthandDeclarationIterator<'a> {
- type Item = PropertyDeclaration;
-
- #[inline]
- fn next(&mut self) -> Option<Self::Item> {
- match *self.all_shorthand {
- AllShorthand::NotSet => None,
- AllShorthand::CSSWideKeyword(ref keyword) => {
- Some(PropertyDeclaration::css_wide_keyword(self.longhands.next()?, *keyword))
- }
- AllShorthand::WithVariables(ref unparsed) => {
- Some(PropertyDeclaration::WithVariables(
- VariableDeclaration {
- id: self.longhands.next()?,
- value: unparsed.clone()
- }
- ))
- }
- }
- }
-}
-
-#[cfg(feature = "gecko")]
-pub use super::gecko::style_structs;
-
-/// The module where all the style structs are defined.
-#[cfg(feature = "servo")]
-pub mod style_structs {
- use fxhash::FxHasher;
- use super::longhands;
- use std::hash::{Hash, Hasher};
- use crate::logical_geometry::WritingMode;
- use crate::media_queries::Device;
- use crate::values::computed::NonNegativeLength;
-
- % for style_struct in data.active_style_structs():
- % if style_struct.name == "Font":
- #[derive(Clone, Debug, MallocSizeOf)]
- #[cfg_attr(feature = "servo", derive(Serialize, Deserialize))]
- % else:
- #[derive(Clone, Debug, MallocSizeOf, PartialEq)]
- % endif
- /// The ${style_struct.name} style struct.
- pub struct ${style_struct.name} {
- % for longhand in style_struct.longhands:
- % if not longhand.logical:
- /// The ${longhand.name} computed value.
- pub ${longhand.ident}: longhands::${longhand.ident}::computed_value::T,
- % endif
- % endfor
- % if style_struct.name == "InheritedText":
- /// The "used" text-decorations that apply to this box.
- ///
- /// FIXME(emilio): This is technically a box-tree concept, and
- /// would be nice to move away from style.
- pub text_decorations_in_effect: crate::values::computed::text::TextDecorationsInEffect,
- % endif
- % if style_struct.name == "Font":
- /// The font hash, used for font caching.
- pub hash: u64,
- % endif
- % if style_struct.name == "Box":
- /// The display value specified by the CSS stylesheets (without any style adjustments),
- /// which is needed for hypothetical layout boxes.
- pub original_display: longhands::display::computed_value::T,
- % endif
- }
- % if style_struct.name == "Font":
- impl PartialEq for Font {
- fn eq(&self, other: &Font) -> bool {
- self.hash == other.hash
- % for longhand in style_struct.longhands:
- && self.${longhand.ident} == other.${longhand.ident}
- % endfor
- }
- }
- % endif
-
- impl ${style_struct.name} {
- % for longhand in style_struct.longhands:
- % if longhand.logical:
- ${helpers.logical_setter(name=longhand.name)}
- % else:
- % if longhand.ident == "display":
- /// Set `display`.
- ///
- /// We need to keep track of the original display for hypothetical boxes,
- /// so we need to special-case this.
- #[allow(non_snake_case)]
- #[inline]
- pub fn set_display(&mut self, v: longhands::display::computed_value::T) {
- self.display = v;
- self.original_display = v;
- }
- % else:
- /// Set ${longhand.name}.
- #[allow(non_snake_case)]
- #[inline]
- pub fn set_${longhand.ident}(&mut self, v: longhands::${longhand.ident}::computed_value::T) {
- self.${longhand.ident} = v;
- }
- % endif
- % if longhand.ident == "display":
- /// Set `display` from other struct.
- ///
- /// Same as `set_display` above.
- /// Thus, we need to special-case this.
- #[allow(non_snake_case)]
- #[inline]
- pub fn copy_display_from(&mut self, other: &Self) {
- self.display = other.display.clone();
- self.original_display = other.display.clone();
- }
- % else:
- /// Set ${longhand.name} from other struct.
- #[allow(non_snake_case)]
- #[inline]
- pub fn copy_${longhand.ident}_from(&mut self, other: &Self) {
- self.${longhand.ident} = other.${longhand.ident}.clone();
- }
- % endif
- /// Reset ${longhand.name} from the initial struct.
- #[allow(non_snake_case)]
- #[inline]
- pub fn reset_${longhand.ident}(&mut self, other: &Self) {
- self.copy_${longhand.ident}_from(other)
- }
-
- /// Get the computed value for ${longhand.name}.
- #[allow(non_snake_case)]
- #[inline]
- pub fn clone_${longhand.ident}(&self) -> longhands::${longhand.ident}::computed_value::T {
- self.${longhand.ident}.clone()
- }
- % endif
- % if longhand.need_index:
- /// If this longhand is indexed, get the number of elements.
- #[allow(non_snake_case)]
- pub fn ${longhand.ident}_count(&self) -> usize {
- self.${longhand.ident}.0.len()
- }
-
- /// If this longhand is indexed, get the element at given
- /// index.
- #[allow(non_snake_case)]
- pub fn ${longhand.ident}_at(&self, index: usize)
- -> longhands::${longhand.ident}::computed_value::SingleComputedValue {
- self.${longhand.ident}.0[index].clone()
- }
- % endif
- % endfor
- % if style_struct.name == "Border":
- % for side in ["top", "right", "bottom", "left"]:
- /// Whether the border-${side} property has nonzero width.
- #[allow(non_snake_case)]
- pub fn border_${side}_has_nonzero_width(&self) -> bool {
- use crate::Zero;
- !self.border_${side}_width.is_zero()
- }
- % endfor
- % elif style_struct.name == "Font":
- /// Computes a font hash in order to be able to cache fonts
- /// effectively in GFX and layout.
- pub fn compute_font_hash(&mut self) {
- // Corresponds to the fields in
- // `gfx::font_template::FontTemplateDescriptor`.
- let mut hasher: FxHasher = Default::default();
- self.font_weight.hash(&mut hasher);
- self.font_stretch.hash(&mut hasher);
- self.font_style.hash(&mut hasher);
- self.font_family.hash(&mut hasher);
- self.hash = hasher.finish()
- }
-
- /// (Servo does not handle MathML, so this just calls copy_font_size_from)
- pub fn inherit_font_size_from(&mut self, parent: &Self,
- _: Option<NonNegativeLength>,
- _: &Device) {
- self.copy_font_size_from(parent);
- }
- /// (Servo does not handle MathML, so this just calls set_font_size)
- pub fn apply_font_size(&mut self,
- v: longhands::font_size::computed_value::T,
- _: &Self,
- _: &Device) -> Option<NonNegativeLength> {
- self.set_font_size(v);
- None
- }
- /// (Servo does not handle MathML, so this does nothing)
- pub fn apply_unconstrained_font_size(&mut self, _: NonNegativeLength) {
- }
-
- % elif style_struct.name == "Outline":
- /// Whether the outline-width property is non-zero.
- #[inline]
- pub fn outline_has_nonzero_width(&self) -> bool {
- use crate::Zero;
- !self.outline_width.is_zero()
- }
- % elif style_struct.name == "Box":
- /// Sets the display property, but without touching original_display,
- /// except when the adjustment comes from root or item display fixups.
- pub fn set_adjusted_display(
- &mut self,
- dpy: longhands::display::computed_value::T,
- is_item_or_root: bool
- ) {
- self.display = dpy;
- if is_item_or_root {
- self.original_display = dpy;
- }
- }
- % endif
- }
-
- % endfor
-}
-
-% for style_struct in data.active_style_structs():
- impl style_structs::${style_struct.name} {
- % for longhand in style_struct.longhands:
- % if longhand.need_index:
- /// Iterate over the values of ${longhand.name}.
- #[allow(non_snake_case)]
- #[inline]
- pub fn ${longhand.ident}_iter(&self) -> ${longhand.camel_case}Iter {
- ${longhand.camel_case}Iter {
- style_struct: self,
- current: 0,
- max: self.${longhand.ident}_count(),
- }
- }
-
- /// Get a value mod `index` for the property ${longhand.name}.
- #[allow(non_snake_case)]
- #[inline]
- pub fn ${longhand.ident}_mod(&self, index: usize)
- -> longhands::${longhand.ident}::computed_value::SingleComputedValue {
- self.${longhand.ident}_at(index % self.${longhand.ident}_count())
- }
-
- /// Clone the computed value for the property.
- #[allow(non_snake_case)]
- #[inline]
- #[cfg(feature = "gecko")]
- pub fn clone_${longhand.ident}(
- &self,
- ) -> longhands::${longhand.ident}::computed_value::T {
- longhands::${longhand.ident}::computed_value::List(
- self.${longhand.ident}_iter().collect()
- )
- }
- % endif
- % endfor
-
- % if style_struct.name == "UI":
- /// Returns whether there is any animation specified with
- /// animation-name other than `none`.
- pub fn specifies_animations(&self) -> bool {
- self.animation_name_iter().any(|name| !name.is_none())
- }
-
- /// Returns whether there are any transitions specified.
- #[cfg(feature = "servo")]
- pub fn specifies_transitions(&self) -> bool {
- (0..self.transition_property_count()).any(|index| {
- let combined_duration =
- self.transition_duration_mod(index).seconds().max(0.) +
- self.transition_delay_mod(index).seconds();
- combined_duration > 0.
- })
- }
-
- /// Returns whether there is any named progress timeline specified with
- /// scroll-timeline-name other than `none`.
- #[cfg(feature = "gecko")]
- pub fn specifies_scroll_timelines(&self) -> bool {
- self.scroll_timeline_name_iter().any(|name| !name.is_none())
- }
-
- /// Returns whether there is any named progress timeline specified with
- /// view-timeline-name other than `none`.
- #[cfg(feature = "gecko")]
- pub fn specifies_view_timelines(&self) -> bool {
- self.view_timeline_name_iter().any(|name| !name.is_none())
- }
-
- /// Returns true if animation properties are equal between styles, but without
- /// considering keyframe data and animation-timeline.
- #[cfg(feature = "servo")]
- pub fn animations_equals(&self, other: &Self) -> bool {
- self.animation_name_iter().eq(other.animation_name_iter()) &&
- self.animation_delay_iter().eq(other.animation_delay_iter()) &&
- self.animation_direction_iter().eq(other.animation_direction_iter()) &&
- self.animation_duration_iter().eq(other.animation_duration_iter()) &&
- self.animation_fill_mode_iter().eq(other.animation_fill_mode_iter()) &&
- self.animation_iteration_count_iter().eq(other.animation_iteration_count_iter()) &&
- self.animation_play_state_iter().eq(other.animation_play_state_iter()) &&
- self.animation_timing_function_iter().eq(other.animation_timing_function_iter())
- }
-
- % elif style_struct.name == "Column":
- /// Whether this is a multicol style.
- #[cfg(feature = "servo")]
- pub fn is_multicol(&self) -> bool {
- !self.column_width.is_auto() || !self.column_count.is_auto()
- }
- % endif
- }
-
- % for longhand in style_struct.longhands:
- % if longhand.need_index:
- /// An iterator over the values of the ${longhand.name} properties.
- pub struct ${longhand.camel_case}Iter<'a> {
- style_struct: &'a style_structs::${style_struct.name},
- current: usize,
- max: usize,
- }
-
- impl<'a> Iterator for ${longhand.camel_case}Iter<'a> {
- type Item = longhands::${longhand.ident}::computed_value::SingleComputedValue;
-
- fn next(&mut self) -> Option<Self::Item> {
- self.current += 1;
- if self.current <= self.max {
- Some(self.style_struct.${longhand.ident}_at(self.current - 1))
- } else {
- None
- }
- }
- }
- % endif
- % endfor
-% endfor
-
-
-#[cfg(feature = "gecko")]
-pub use super::gecko::{ComputedValues, ComputedValuesInner};
-
-#[cfg(feature = "servo")]
-#[cfg_attr(feature = "servo", derive(Clone, Debug))]
-/// Actual data of ComputedValues, to match up with Gecko
-pub struct ComputedValuesInner {
- % for style_struct in data.active_style_structs():
- ${style_struct.ident}: Arc<style_structs::${style_struct.name}>,
- % endfor
- custom_properties: Option<Arc<crate::custom_properties::CustomPropertiesMap>>,
- /// The writing mode of this computed values struct.
- pub writing_mode: WritingMode,
-
- /// A set of flags we use to store misc information regarding this style.
- pub flags: ComputedValueFlags,
-
- /// The rule node representing the ordered list of rules matched for this
- /// node. Can be None for default values and text nodes. This is
- /// essentially an optimization to avoid referencing the root rule node.
- pub rules: Option<StrongRuleNode>,
-
- /// The element's computed values if visited, only computed if there's a
- /// relevant link for this element. A element's "relevant link" is the
- /// element being matched if it is a link or the nearest ancestor link.
- visited_style: Option<Arc<ComputedValues>>,
-}
-
-/// The struct that Servo uses to represent computed values.
-///
-/// This struct contains an immutable atomically-reference-counted pointer to
-/// every kind of style struct.
-///
-/// When needed, the structs may be copied in order to get mutated.
-#[cfg(feature = "servo")]
-#[cfg_attr(feature = "servo", derive(Clone, Debug))]
-pub struct ComputedValues {
- /// The actual computed values
- ///
- /// In Gecko the outer ComputedValues is actually a ComputedStyle, whereas
- /// ComputedValuesInner is the core set of computed values.
- ///
- /// We maintain this distinction in servo to reduce the amount of special
- /// casing.
- inner: ComputedValuesInner,
-
- /// The pseudo-element that we're using.
- pseudo: Option<PseudoElement>,
-}
-
-impl ComputedValues {
- /// Returns the pseudo-element that this style represents.
- #[cfg(feature = "servo")]
- pub fn pseudo(&self) -> Option<<&PseudoElement> {
- self.pseudo.as_ref()
- }
-
- /// Returns true if this is the style for a pseudo-element.
- #[cfg(feature = "servo")]
- pub fn is_pseudo_style(&self) -> bool {
- self.pseudo().is_some()
- }
-
- /// Returns whether this style's display value is equal to contents.
- pub fn is_display_contents(&self) -> bool {
- self.clone_display().is_contents()
- }
-
- /// Gets a reference to the rule node. Panic if no rule node exists.
- pub fn rules(&self) -> &StrongRuleNode {
- self.rules.as_ref().unwrap()
- }
-
- /// Returns the visited rules, if applicable.
- pub fn visited_rules(&self) -> Option<<&StrongRuleNode> {
- self.visited_style().and_then(|s| s.rules.as_ref())
- }
-
- /// Gets a reference to the custom properties map (if one exists).
- pub fn custom_properties(&self) -> Option<<&Arc<crate::custom_properties::CustomPropertiesMap>> {
- self.custom_properties.as_ref()
- }
-
- /// Returns whether we have the same custom properties as another style.
- ///
- /// This should effectively be just:
- ///
- /// self.custom_properties() == other.custom_properties()
- ///
- /// But that's not really the case because IndexMap equality doesn't
- /// consider ordering, which we have to account for. Also, for the same
- /// reason, IndexMap equality comparisons are slower than needed.
- ///
- /// See https://github.com/bluss/indexmap/issues/153
- pub fn custom_properties_equal(&self, other: &Self) -> bool {
- match (self.custom_properties(), other.custom_properties()) {
- (Some(l), Some(r)) => {
- l.len() == r.len() && l.iter().zip(r.iter()).all(|((k1, v1), (k2, v2))| k1 == k2 && v1 == v2)
- },
- (None, None) => true,
- _ => false,
- }
- }
-
-% for prop in data.longhands:
- /// Gets the computed value of a given property.
- #[inline(always)]
- #[allow(non_snake_case)]
- pub fn clone_${prop.ident}(
- &self,
- ) -> longhands::${prop.ident}::computed_value::T {
- self.get_${prop.style_struct.ident.strip("_")}()
- % if prop.logical:
- .clone_${prop.ident}(self.writing_mode)
- % else:
- .clone_${prop.ident}()
- % endif
- }
-% endfor
-
- /// Writes the (resolved or computed) value of the given longhand as a string in `dest`.
- ///
- /// TODO(emilio): We should move all the special resolution from
- /// nsComputedDOMStyle to ToResolvedValue instead.
- pub fn computed_or_resolved_value(
- &self,
- property_id: LonghandId,
- context: Option<<&resolved::Context>,
- dest: &mut CssStringWriter,
- ) -> fmt::Result {
- use crate::values::resolved::ToResolvedValue;
- let mut dest = CssWriter::new(dest);
- match property_id {
- % for specified_type, props in groupby(data.longhands, key=lambda x: x.specified_type()):
- <% props = list(props) %>
- ${" |\n".join("LonghandId::{}".format(p.camel_case) for p in props)} => {
- let value = match property_id {
- % for prop in props:
- LonghandId::${prop.camel_case} => self.clone_${prop.ident}(),
- % endfor
- _ => unsafe { debug_unreachable!() },
- };
- if let Some(c) = context {
- value.to_resolved_value(c).to_css(&mut dest)
- } else {
- value.to_css(&mut dest)
- }
- }
- % endfor
- }
- }
-
- /// Returns the given longhand's resolved value as a property declaration.
- pub fn computed_or_resolved_declaration(
- &self,
- property_id: LonghandId,
- context: Option<<&resolved::Context>,
- ) -> PropertyDeclaration {
- use crate::values::resolved::ToResolvedValue;
- use crate::values::computed::ToComputedValue;
- match property_id {
- % for specified_type, props in groupby(data.longhands, key=lambda x: x.specified_type()):
- <% props = list(props) %>
- ${" |\n".join("LonghandId::{}".format(p.camel_case) for p in props)} => {
- let mut computed_value = match property_id {
- % for prop in props:
- LonghandId::${prop.camel_case} => self.clone_${prop.ident}(),
- % endfor
- _ => unsafe { debug_unreachable!() },
- };
- if let Some(c) = context {
- let resolved = computed_value.to_resolved_value(c);
- computed_value = ToResolvedValue::from_resolved_value(resolved);
- }
- let specified = ToComputedValue::from_computed_value(&computed_value);
- % if props[0].boxed:
- let specified = Box::new(specified);
- % endif
- % if len(props) == 1:
- PropertyDeclaration::${props[0].camel_case}(specified)
- % else:
- unsafe {
- let mut out = mem::MaybeUninit::uninit();
- ptr::write(
- out.as_mut_ptr() as *mut PropertyDeclarationVariantRepr<${specified_type}>,
- PropertyDeclarationVariantRepr {
- tag: property_id as u16,
- value: specified,
- },
- );
- out.assume_init()
- }
- % endif
- }
- % endfor
- }
- }
-
- /// Resolves the currentColor keyword.
- ///
- /// Any color value from computed values (except for the 'color' property
- /// itself) should go through this method.
- ///
- /// Usage example:
- /// let top_color =
- /// style.resolve_color(style.get_border().clone_border_top_color());
- #[inline]
- pub fn resolve_color(&self, color: computed::Color) -> crate::color::AbsoluteColor {
- let current_color = self.get_inherited_text().clone_color();
- color.resolve_to_absolute(&current_color)
- }
-
- /// Returns which longhand properties have different values in the two
- /// ComputedValues.
- #[cfg(feature = "gecko_debug")]
- pub fn differing_properties(&self, other: &ComputedValues) -> LonghandIdSet {
- let mut set = LonghandIdSet::new();
- % for prop in data.longhands:
- if self.clone_${prop.ident}() != other.clone_${prop.ident}() {
- set.insert(LonghandId::${prop.camel_case});
- }
- % endfor
- set
- }
-
- /// Create a `TransitionPropertyIterator` for this styles transition properties.
- pub fn transition_properties<'a>(
- &'a self
- ) -> animated_properties::TransitionPropertyIterator<'a> {
- animated_properties::TransitionPropertyIterator::from_style(self)
- }
-}
-
-#[cfg(feature = "servo")]
-impl ComputedValues {
- /// Create a new refcounted `ComputedValues`
- pub fn new(
- pseudo: Option<<&PseudoElement>,
- custom_properties: Option<Arc<crate::custom_properties::CustomPropertiesMap>>,
- writing_mode: WritingMode,
- flags: ComputedValueFlags,
- rules: Option<StrongRuleNode>,
- visited_style: Option<Arc<ComputedValues>>,
- % for style_struct in data.active_style_structs():
- ${style_struct.ident}: Arc<style_structs::${style_struct.name}>,
- % endfor
- ) -> Arc<Self> {
- Arc::new(Self {
- inner: ComputedValuesInner {
- custom_properties,
- writing_mode,
- rules,
- visited_style,
- flags,
- % for style_struct in data.active_style_structs():
- ${style_struct.ident},
- % endfor
- },
- pseudo: pseudo.cloned(),
- })
- }
-
- /// Get the initial computed values.
- pub fn initial_values() -> &'static Self { &*INITIAL_SERVO_VALUES }
-
- /// Converts the computed values to an Arc<> from a reference.
- pub fn to_arc(&self) -> Arc<Self> {
- // SAFETY: We're guaranteed to be allocated as an Arc<> since the
- // functions above are the only ones that create ComputedValues
- // instances in Servo (and that must be the case since ComputedValues'
- // member is private).
- unsafe { Arc::from_raw_addrefed(self) }
- }
-
- /// Serializes the computed value of this property as a string.
- pub fn computed_value_to_string(&self, property: PropertyDeclarationId) -> String {
- match property {
- PropertyDeclarationId::Longhand(id) => {
- let context = resolved::Context {
- style: self,
- };
- let mut s = String::new();
- self.computed_or_resolved_value(
- id,
- Some(&context),
- &mut s
- ).unwrap();
- s
- }
- PropertyDeclarationId::Custom(name) => {
- self.custom_properties
- .as_ref()
- .and_then(|map| map.get(name))
- .map_or(String::new(), |value| value.to_css_string())
- }
- }
- }
-}
-
-#[cfg(feature = "servo")]
-impl ops::Deref for ComputedValues {
- type Target = ComputedValuesInner;
- fn deref(&self) -> &ComputedValuesInner {
- &self.inner
- }
-}
-
-#[cfg(feature = "servo")]
-impl ops::DerefMut for ComputedValues {
- fn deref_mut(&mut self) -> &mut ComputedValuesInner {
- &mut self.inner
- }
-}
-
-#[cfg(feature = "servo")]
-impl ComputedValuesInner {
- /// Returns the visited style, if any.
- pub fn visited_style(&self) -> Option<<&ComputedValues> {
- self.visited_style.as_deref()
- }
-
- % for style_struct in data.active_style_structs():
- /// Clone the ${style_struct.name} struct.
- #[inline]
- pub fn clone_${style_struct.name_lower}(&self) -> Arc<style_structs::${style_struct.name}> {
- self.${style_struct.ident}.clone()
- }
-
- /// Get a immutable reference to the ${style_struct.name} struct.
- #[inline]
- pub fn get_${style_struct.name_lower}(&self) -> &style_structs::${style_struct.name} {
- &self.${style_struct.ident}
- }
-
- /// Get a mutable reference to the ${style_struct.name} struct.
- #[inline]
- pub fn mutate_${style_struct.name_lower}(&mut self) -> &mut style_structs::${style_struct.name} {
- Arc::make_mut(&mut self.${style_struct.ident})
- }
- % endfor
-
- /// Gets a reference to the rule node. Panic if no rule node exists.
- pub fn rules(&self) -> &StrongRuleNode {
- self.rules.as_ref().unwrap()
- }
-
- #[inline]
- /// Returns whether the "content" property for the given style is completely
- /// ineffective, and would yield an empty `::before` or `::after`
- /// pseudo-element.
- pub fn ineffective_content_property(&self) -> bool {
- use crate::values::generics::counters::Content;
- match self.get_counters().content {
- Content::Normal | Content::None => true,
- Content::Items(ref items) => items.is_empty(),
- }
- }
-
- /// Whether the current style or any of its ancestors is multicolumn.
- #[inline]
- pub fn can_be_fragmented(&self) -> bool {
- self.flags.contains(ComputedValueFlags::CAN_BE_FRAGMENTED)
- }
-
- /// Whether the current style is multicolumn.
- #[inline]
- pub fn is_multicol(&self) -> bool {
- self.get_column().is_multicol()
- }
-
- /// Get the logical computed inline size.
- #[inline]
- pub fn content_inline_size(&self) -> &computed::Size {
- let position_style = self.get_position();
- if self.writing_mode.is_vertical() {
- &position_style.height
- } else {
- &position_style.width
- }
- }
-
- /// Get the logical computed block size.
- #[inline]
- pub fn content_block_size(&self) -> &computed::Size {
- let position_style = self.get_position();
- if self.writing_mode.is_vertical() { &position_style.width } else { &position_style.height }
- }
-
- /// Get the logical computed min inline size.
- #[inline]
- pub fn min_inline_size(&self) -> &computed::Size {
- let position_style = self.get_position();
- if self.writing_mode.is_vertical() { &position_style.min_height } else { &position_style.min_width }
- }
-
- /// Get the logical computed min block size.
- #[inline]
- pub fn min_block_size(&self) -> &computed::Size {
- let position_style = self.get_position();
- if self.writing_mode.is_vertical() { &position_style.min_width } else { &position_style.min_height }
- }
-
- /// Get the logical computed max inline size.
- #[inline]
- pub fn max_inline_size(&self) -> &computed::MaxSize {
- let position_style = self.get_position();
- if self.writing_mode.is_vertical() { &position_style.max_height } else { &position_style.max_width }
- }
-
- /// Get the logical computed max block size.
- #[inline]
- pub fn max_block_size(&self) -> &computed::MaxSize {
- let position_style = self.get_position();
- if self.writing_mode.is_vertical() { &position_style.max_width } else { &position_style.max_height }
- }
-
- /// Get the logical computed padding for this writing mode.
- #[inline]
- pub fn logical_padding(&self) -> LogicalMargin<<&computed::LengthPercentage> {
- let padding_style = self.get_padding();
- LogicalMargin::from_physical(self.writing_mode, SideOffsets2D::new(
- &padding_style.padding_top.0,
- &padding_style.padding_right.0,
- &padding_style.padding_bottom.0,
- &padding_style.padding_left.0,
- ))
- }
-
- /// Get the logical border width
- #[inline]
- pub fn border_width_for_writing_mode(&self, writing_mode: WritingMode) -> LogicalMargin<Au> {
- let border_style = self.get_border();
- LogicalMargin::from_physical(writing_mode, SideOffsets2D::new(
- Au::from(border_style.border_top_width),
- Au::from(border_style.border_right_width),
- Au::from(border_style.border_bottom_width),
- Au::from(border_style.border_left_width),
- ))
- }
-
- /// Gets the logical computed border widths for this style.
- #[inline]
- pub fn logical_border_width(&self) -> LogicalMargin<Au> {
- self.border_width_for_writing_mode(self.writing_mode)
- }
-
- /// Gets the logical computed margin from this style.
- #[inline]
- pub fn logical_margin(&self) -> LogicalMargin<<&computed::LengthPercentageOrAuto> {
- let margin_style = self.get_margin();
- LogicalMargin::from_physical(self.writing_mode, SideOffsets2D::new(
- &margin_style.margin_top,
- &margin_style.margin_right,
- &margin_style.margin_bottom,
- &margin_style.margin_left,
- ))
- }
-
- /// Gets the logical position from this style.
- #[inline]
- pub fn logical_position(&self) -> LogicalMargin<<&computed::LengthPercentageOrAuto> {
- // FIXME(SimonSapin): should be the writing mode of the containing block, maybe?
- let position_style = self.get_position();
- LogicalMargin::from_physical(self.writing_mode, SideOffsets2D::new(
- &position_style.top,
- &position_style.right,
- &position_style.bottom,
- &position_style.left,
- ))
- }
-
- /// Return true if the effects force the transform style to be Flat
- pub fn overrides_transform_style(&self) -> bool {
- use crate::computed_values::mix_blend_mode::T as MixBlendMode;
-
- let effects = self.get_effects();
- // TODO(gw): Add clip-path, isolation, mask-image, mask-border-source when supported.
- effects.opacity < 1.0 ||
- !effects.filter.0.is_empty() ||
- !effects.clip.is_auto() ||
- effects.mix_blend_mode != MixBlendMode::Normal
- }
-
- /// <https://drafts.csswg.org/css-transforms/#grouping-property-values>
- pub fn get_used_transform_style(&self) -> computed_values::transform_style::T {
- use crate::computed_values::transform_style::T as TransformStyle;
-
- let box_ = self.get_box();
-
- if self.overrides_transform_style() {
- TransformStyle::Flat
- } else {
- // Return the computed value if not overridden by the above exceptions
- box_.transform_style
- }
- }
-
- /// Whether given this transform value, the compositor would require a
- /// layer.
- pub fn transform_requires_layer(&self) -> bool {
- use crate::values::generics::transform::TransformOperation;
- // Check if the transform matrix is 2D or 3D
- for transform in &*self.get_box().transform.0 {
- match *transform {
- TransformOperation::Perspective(..) => {
- return true;
- }
- TransformOperation::Matrix3D(m) => {
- // See http://dev.w3.org/csswg/css-transforms/#2d-matrix
- if m.m31 != 0.0 || m.m32 != 0.0 ||
- m.m13 != 0.0 || m.m23 != 0.0 ||
- m.m43 != 0.0 || m.m14 != 0.0 ||
- m.m24 != 0.0 || m.m34 != 0.0 ||
- m.m33 != 1.0 || m.m44 != 1.0 {
- return true;
- }
- }
- TransformOperation::Translate3D(_, _, z) |
- TransformOperation::TranslateZ(z) => {
- if z.px() != 0. {
- return true;
- }
- }
- _ => {}
- }
- }
-
- // Neither perspective nor transform present
- false
- }
-}
-
-/// A reference to a style struct of the parent, or our own style struct.
-pub enum StyleStructRef<'a, T: 'static> {
- /// A borrowed struct from the parent, for example, for inheriting style.
- Borrowed(&'a T),
- /// An owned struct, that we've already mutated.
- Owned(UniqueArc<T>),
- /// Temporarily vacated, will panic if accessed
- Vacated,
-}
-
-impl<'a, T: 'a> StyleStructRef<'a, T>
-where
- T: Clone,
-{
- /// Ensure a mutable reference of this value exists, either cloning the
- /// borrowed value, or returning the owned one.
- pub fn mutate(&mut self) -> &mut T {
- if let StyleStructRef::Borrowed(v) = *self {
- *self = StyleStructRef::Owned(UniqueArc::new(v.clone()));
- }
-
- match *self {
- StyleStructRef::Owned(ref mut v) => v,
- StyleStructRef::Borrowed(..) => unreachable!(),
- StyleStructRef::Vacated => panic!("Accessed vacated style struct")
- }
- }
-
- /// Whether this is pointer-equal to the struct we're going to copy the
- /// value from.
- ///
- /// This is used to avoid allocations when people write stuff like `font:
- /// inherit` or such `all: initial`.
- #[inline]
- pub fn ptr_eq(&self, struct_to_copy_from: &T) -> bool {
- match *self {
- StyleStructRef::Owned(..) => false,
- StyleStructRef::Borrowed(s) => {
- s as *const T == struct_to_copy_from as *const T
- }
- StyleStructRef::Vacated => panic!("Accessed vacated style struct")
- }
- }
-
- /// Extract a unique Arc from this struct, vacating it.
- ///
- /// The vacated state is a transient one, please put the Arc back
- /// when done via `put()`. This function is to be used to separate
- /// the struct being mutated from the computed context
- pub fn take(&mut self) -> UniqueArc<T> {
- use std::mem::replace;
- let inner = replace(self, StyleStructRef::Vacated);
-
- match inner {
- StyleStructRef::Owned(arc) => arc,
- StyleStructRef::Borrowed(s) => UniqueArc::new(s.clone()),
- StyleStructRef::Vacated => panic!("Accessed vacated style struct"),
- }
- }
-
- /// Replace vacated ref with an arc
- pub fn put(&mut self, arc: UniqueArc<T>) {
- debug_assert!(matches!(*self, StyleStructRef::Vacated));
- *self = StyleStructRef::Owned(arc);
- }
-
- /// Get a mutable reference to the owned struct, or `None` if the struct
- /// hasn't been mutated.
- pub fn get_if_mutated(&mut self) -> Option<<&mut T> {
- match *self {
- StyleStructRef::Owned(ref mut v) => Some(v),
- StyleStructRef::Borrowed(..) => None,
- StyleStructRef::Vacated => panic!("Accessed vacated style struct")
- }
- }
-
- /// Returns an `Arc` to the internal struct, constructing one if
- /// appropriate.
- pub fn build(self) -> Arc<T> {
- match self {
- StyleStructRef::Owned(v) => v.shareable(),
- // SAFETY: We know all style structs are arc-allocated.
- StyleStructRef::Borrowed(v) => unsafe { Arc::from_raw_addrefed(v) },
- StyleStructRef::Vacated => panic!("Accessed vacated style struct")
- }
- }
-}
-
-impl<'a, T: 'a> ops::Deref for StyleStructRef<'a, T> {
- type Target = T;
-
- fn deref(&self) -> &T {
- match *self {
- StyleStructRef::Owned(ref v) => &**v,
- StyleStructRef::Borrowed(v) => v,
- StyleStructRef::Vacated => panic!("Accessed vacated style struct")
- }
- }
-}
-
-/// A type used to compute a struct with minimal overhead.
-///
-/// This allows holding references to the parent/default computed values without
-/// actually cloning them, until we either build the style, or mutate the
-/// inherited value.
-pub struct StyleBuilder<'a> {
- /// The device we're using to compute style.
- ///
- /// This provides access to viewport unit ratios, etc.
- pub device: &'a Device,
-
- /// The style we're inheriting from.
- ///
- /// This is effectively
- /// `parent_style.unwrap_or(device.default_computed_values())`.
- inherited_style: &'a ComputedValues,
-
- /// The style we're inheriting from for properties that don't inherit from
- /// ::first-line. This is the same as inherited_style, unless
- /// inherited_style is a ::first-line style.
- inherited_style_ignoring_first_line: &'a ComputedValues,
-
- /// The style we're getting reset structs from.
- reset_style: &'a ComputedValues,
-
- /// The rule node representing the ordered list of rules matched for this
- /// node.
- pub rules: Option<StrongRuleNode>,
-
- custom_properties: Option<Arc<crate::custom_properties::CustomPropertiesMap>>,
-
- /// The pseudo-element this style will represent.
- pub pseudo: Option<<&'a PseudoElement>,
-
- /// Whether we have mutated any reset structs since the the last time
- /// `clear_modified_reset` was called. This is used to tell whether the
- /// `StyleAdjuster` did any work.
- modified_reset: bool,
-
- /// Whether this is the style for the root element.
- pub is_root_element: bool,
-
- /// The writing mode flags.
- ///
- /// TODO(emilio): Make private.
- pub writing_mode: WritingMode,
-
- /// Flags for the computed value.
- pub flags: Cell<ComputedValueFlags>,
-
- /// The element's style if visited, only computed if there's a relevant link
- /// for this element. A element's "relevant link" is the element being
- /// matched if it is a link or the nearest ancestor link.
- pub visited_style: Option<Arc<ComputedValues>>,
- % for style_struct in data.active_style_structs():
- ${style_struct.ident}: StyleStructRef<'a, style_structs::${style_struct.name}>,
- % endfor
-}
-
-impl<'a> StyleBuilder<'a> {
- /// Trivially construct a `StyleBuilder`.
- pub(super) fn new(
- device: &'a Device,
- parent_style: Option<<&'a ComputedValues>,
- parent_style_ignoring_first_line: Option<<&'a ComputedValues>,
- pseudo: Option<<&'a PseudoElement>,
- rules: Option<StrongRuleNode>,
- custom_properties: Option<Arc<crate::custom_properties::CustomPropertiesMap>>,
- is_root_element: bool,
- ) -> Self {
- debug_assert_eq!(parent_style.is_some(), parent_style_ignoring_first_line.is_some());
- #[cfg(feature = "gecko")]
- debug_assert!(parent_style.is_none() ||
- std::ptr::eq(parent_style.unwrap(),
- parent_style_ignoring_first_line.unwrap()) ||
- parent_style.unwrap().is_first_line_style());
- let reset_style = device.default_computed_values();
- let inherited_style = parent_style.unwrap_or(reset_style);
- let inherited_style_ignoring_first_line = parent_style_ignoring_first_line.unwrap_or(reset_style);
-
- let flags = inherited_style.flags.inherited();
-
- StyleBuilder {
- device,
- inherited_style,
- inherited_style_ignoring_first_line,
- reset_style,
- pseudo,
- rules,
- modified_reset: false,
- is_root_element,
- custom_properties,
- writing_mode: inherited_style.writing_mode,
- flags: Cell::new(flags),
- visited_style: None,
- % for style_struct in data.active_style_structs():
- % if style_struct.inherited:
- ${style_struct.ident}: StyleStructRef::Borrowed(inherited_style.get_${style_struct.name_lower}()),
- % else:
- ${style_struct.ident}: StyleStructRef::Borrowed(reset_style.get_${style_struct.name_lower}()),
- % endif
- % endfor
- }
- }
-
- /// NOTE(emilio): This is done so we can compute relative units with respect
- /// to the parent style, but all the early properties / writing-mode / etc
- /// are already set to the right ones on the kid.
- ///
- /// Do _not_ actually call this to construct a style, this should mostly be
- /// used for animations.
- pub fn for_animation(
- device: &'a Device,
- style_to_derive_from: &'a ComputedValues,
- parent_style: Option<<&'a ComputedValues>,
- ) -> Self {
- let reset_style = device.default_computed_values();
- let inherited_style = parent_style.unwrap_or(reset_style);
- #[cfg(feature = "gecko")]
- debug_assert!(parent_style.is_none() ||
- !parent_style.unwrap().is_first_line_style());
- StyleBuilder {
- device,
- inherited_style,
- // None of our callers pass in ::first-line parent styles.
- inherited_style_ignoring_first_line: inherited_style,
- reset_style,
- pseudo: None,
- modified_reset: false,
- is_root_element: false,
- rules: None,
- custom_properties: style_to_derive_from.custom_properties().cloned(),
- writing_mode: style_to_derive_from.writing_mode,
- flags: Cell::new(style_to_derive_from.flags),
- visited_style: None,
- % for style_struct in data.active_style_structs():
- ${style_struct.ident}: StyleStructRef::Borrowed(
- style_to_derive_from.get_${style_struct.name_lower}()
- ),
- % endfor
- }
- }
-
- /// Copy the reset properties from `style`.
- pub fn copy_reset_from(&mut self, style: &'a ComputedValues) {
- % for style_struct in data.active_style_structs():
- % if not style_struct.inherited:
- self.${style_struct.ident} =
- StyleStructRef::Borrowed(style.get_${style_struct.name_lower}());
- % endif
- % endfor
- }
-
- % for property in data.longhands:
- % if not property.style_struct.inherited:
- /// Inherit `${property.ident}` from our parent style.
- #[allow(non_snake_case)]
- pub fn inherit_${property.ident}(&mut self) {
- let inherited_struct =
- self.inherited_style_ignoring_first_line
- .get_${property.style_struct.name_lower}();
-
- self.modified_reset = true;
- self.add_flags(ComputedValueFlags::INHERITS_RESET_STYLE);
-
- % if property.ident == "content":
- self.add_flags(ComputedValueFlags::CONTENT_DEPENDS_ON_INHERITED_STYLE);
- % endif
-
- % if property.ident == "display":
- self.add_flags(ComputedValueFlags::DISPLAY_DEPENDS_ON_INHERITED_STYLE);
- % endif
-
- if self.${property.style_struct.ident}.ptr_eq(inherited_struct) {
- return;
- }
-
- self.${property.style_struct.ident}.mutate()
- .copy_${property.ident}_from(
- inherited_struct,
- % if property.logical:
- self.writing_mode,
- % endif
- );
- }
- % else:
- /// Reset `${property.ident}` to the initial value.
- #[allow(non_snake_case)]
- pub fn reset_${property.ident}(&mut self) {
- let reset_struct =
- self.reset_style.get_${property.style_struct.name_lower}();
-
- if self.${property.style_struct.ident}.ptr_eq(reset_struct) {
- return;
- }
-
- self.${property.style_struct.ident}.mutate()
- .reset_${property.ident}(
- reset_struct,
- % if property.logical:
- self.writing_mode,
- % endif
- );
- }
- % endif
-
- % if not property.is_vector or property.simple_vector_bindings or engine == "servo":
- /// Set the `${property.ident}` to the computed value `value`.
- #[allow(non_snake_case)]
- pub fn set_${property.ident}(
- &mut self,
- value: longhands::${property.ident}::computed_value::T
- ) {
- % if not property.style_struct.inherited:
- self.modified_reset = true;
- % endif
-
- self.${property.style_struct.ident}.mutate()
- .set_${property.ident}(
- value,
- % if property.logical:
- self.writing_mode,
- % endif
- );
- }
- % endif
- % endfor
- <% del property %>
-
- /// Inherits style from the parent element, accounting for the default
- /// computed values that need to be provided as well.
- pub fn for_inheritance(
- device: &'a Device,
- parent: Option<<&'a ComputedValues>,
- pseudo: Option<<&'a PseudoElement>,
- ) -> Self {
- // Rebuild the visited style from the parent, ensuring that it will also
- // not have rules. This matches the unvisited style that will be
- // produced by this builder. This assumes that the caller doesn't need
- // to adjust or process visited style, so we can just build visited
- // style here for simplicity.
- let visited_style = parent.and_then(|parent| {
- parent.visited_style().map(|style| {
- Self::for_inheritance(
- device,
- Some(style),
- pseudo,
- ).build()
- })
- });
- let mut ret = Self::new(
- device,
- parent,
- parent,
- pseudo,
- /* rules = */ None,
- parent.and_then(|p| p.custom_properties().cloned()),
- /* is_root_element = */ false,
- );
- ret.visited_style = visited_style;
- ret
- }
-
- /// Returns whether we have a visited style.
- pub fn has_visited_style(&self) -> bool {
- self.visited_style.is_some()
- }
-
- /// Returns whether we're a pseudo-elements style.
- pub fn is_pseudo_element(&self) -> bool {
- self.pseudo.map_or(false, |p| !p.is_anon_box())
- }
-
- /// Returns the style we're getting reset properties from.
- pub fn default_style(&self) -> &'a ComputedValues {
- self.reset_style
- }
-
- % for style_struct in data.active_style_structs():
- /// Gets an immutable view of the current `${style_struct.name}` style.
- pub fn get_${style_struct.name_lower}(&self) -> &style_structs::${style_struct.name} {
- &self.${style_struct.ident}
- }
-
- /// Gets a mutable view of the current `${style_struct.name}` style.
- pub fn mutate_${style_struct.name_lower}(&mut self) -> &mut style_structs::${style_struct.name} {
- % if not style_struct.inherited:
- self.modified_reset = true;
- % endif
- self.${style_struct.ident}.mutate()
- }
-
- /// Gets a mutable view of the current `${style_struct.name}` style.
- pub fn take_${style_struct.name_lower}(&mut self) -> UniqueArc<style_structs::${style_struct.name}> {
- % if not style_struct.inherited:
- self.modified_reset = true;
- % endif
- self.${style_struct.ident}.take()
- }
-
- /// Gets a mutable view of the current `${style_struct.name}` style.
- pub fn put_${style_struct.name_lower}(&mut self, s: UniqueArc<style_structs::${style_struct.name}>) {
- self.${style_struct.ident}.put(s)
- }
-
- /// Gets a mutable view of the current `${style_struct.name}` style,
- /// only if it's been mutated before.
- pub fn get_${style_struct.name_lower}_if_mutated(&mut self)
- -> Option<<&mut style_structs::${style_struct.name}> {
- self.${style_struct.ident}.get_if_mutated()
- }
-
- /// Reset the current `${style_struct.name}` style to its default value.
- pub fn reset_${style_struct.name_lower}_struct(&mut self) {
- self.${style_struct.ident} =
- StyleStructRef::Borrowed(self.reset_style.get_${style_struct.name_lower}());
- }
- % endfor
- <% del style_struct %>
-
- /// Returns whether this computed style represents a floated object.
- pub fn is_floating(&self) -> bool {
- self.get_box().clone_float().is_floating()
- }
-
- /// Returns whether this computed style represents an absolutely-positioned
- /// object.
- pub fn is_absolutely_positioned(&self) -> bool {
- self.get_box().clone_position().is_absolutely_positioned()
- }
-
- /// Whether this style has a top-layer style.
- #[cfg(feature = "servo")]
- pub fn in_top_layer(&self) -> bool {
- matches!(self.get_box().clone__servo_top_layer(),
- longhands::_servo_top_layer::computed_value::T::Top)
- }
-
- /// Whether this style has a top-layer style.
- #[cfg(feature = "gecko")]
- pub fn in_top_layer(&self) -> bool {
- matches!(self.get_box().clone__moz_top_layer(),
- longhands::_moz_top_layer::computed_value::T::Top)
- }
-
- /// Clears the "have any reset structs been modified" flag.
- pub fn clear_modified_reset(&mut self) {
- self.modified_reset = false;
- }
-
- /// Returns whether we have mutated any reset structs since the the last
- /// time `clear_modified_reset` was called.
- pub fn modified_reset(&self) -> bool {
- self.modified_reset
- }
-
- /// Return the current flags.
- #[inline]
- pub fn flags(&self) -> ComputedValueFlags {
- self.flags.get()
- }
-
- /// Add a flag to the current builder.
- #[inline]
- pub fn add_flags(&self, flag: ComputedValueFlags) {
- let flags = self.flags() | flag;
- self.flags.set(flags);
- }
-
- /// Removes a flag to the current builder.
- #[inline]
- pub fn remove_flags(&self, flag: ComputedValueFlags) {
- let flags = self.flags() & !flag;
- self.flags.set(flags);
- }
-
- /// Turns this `StyleBuilder` into a proper `ComputedValues` instance.
- pub fn build(self) -> Arc<ComputedValues> {
- ComputedValues::new(
- self.pseudo,
- self.custom_properties,
- self.writing_mode,
- self.flags.get(),
- self.rules,
- self.visited_style,
- % for style_struct in data.active_style_structs():
- self.${style_struct.ident}.build(),
- % endfor
- )
- }
-
- /// Get the custom properties map if necessary.
- pub fn custom_properties(&self) -> Option<<&Arc<crate::custom_properties::CustomPropertiesMap>> {
- self.custom_properties.as_ref()
- }
-
- /// Access to various information about our inherited styles. We don't
- /// expose an inherited ComputedValues directly, because in the
- /// ::first-line case some of the inherited information needs to come from
- /// one ComputedValues instance and some from a different one.
-
- /// Inherited writing-mode.
- pub fn inherited_writing_mode(&self) -> &WritingMode {
- &self.inherited_style.writing_mode
- }
-
- /// The computed value flags of our parent.
- #[inline]
- pub fn get_parent_flags(&self) -> ComputedValueFlags {
- self.inherited_style.flags
- }
-
- /// And access to inherited style structs.
- % for style_struct in data.active_style_structs():
- /// Gets our inherited `${style_struct.name}`. We don't name these
- /// accessors `inherited_${style_struct.name_lower}` because we already
- /// have things like "box" vs "inherited_box" as struct names. Do the
- /// next-best thing and call them `parent_${style_struct.name_lower}`
- /// instead.
- pub fn get_parent_${style_struct.name_lower}(&self) -> &style_structs::${style_struct.name} {
- % if style_struct.inherited:
- self.inherited_style.get_${style_struct.name_lower}()
- % else:
- self.inherited_style_ignoring_first_line.get_${style_struct.name_lower}()
- % endif
- }
- % endfor
-}
-
-#[cfg(feature = "servo")]
-pub use self::lazy_static_module::INITIAL_SERVO_VALUES;
-
-// Use a module to work around #[cfg] on lazy_static! not being applied to every generated item.
-#[cfg(feature = "servo")]
-#[allow(missing_docs)]
-mod lazy_static_module {
- use crate::logical_geometry::WritingMode;
- use crate::computed_value_flags::ComputedValueFlags;
- use servo_arc::Arc;
- use super::{ComputedValues, ComputedValuesInner, longhands, style_structs};
-
- lazy_static! {
- /// The initial values for all style structs as defined by the specification.
- pub static ref INITIAL_SERVO_VALUES : Arc<ComputedValues> = Arc::new(ComputedValues {
- inner: ComputedValuesInner {
- % for style_struct in data.active_style_structs():
- ${style_struct.ident}: Arc::new(style_structs::${style_struct.name} {
- % for longhand in style_struct.longhands:
- % if not longhand.logical:
- ${longhand.ident}: longhands::${longhand.ident}::get_initial_value(),
- % endif
- % endfor
- % if style_struct.name == "InheritedText":
- text_decorations_in_effect:
- crate::values::computed::text::TextDecorationsInEffect::default(),
- % endif
- % if style_struct.name == "Font":
- hash: 0,
- % endif
- % if style_struct.name == "Box":
- original_display: longhands::display::get_initial_value(),
- % endif
- }),
- % endfor
- custom_properties: None,
- writing_mode: WritingMode::empty(),
- rules: None,
- visited_style: None,
- flags: ComputedValueFlags::empty(),
- },
- pseudo: None,
- });
- }
-}
-
-/// A per-longhand function that performs the CSS cascade for that longhand.
-pub type CascadePropertyFn =
- extern "Rust" fn(
- declaration: &PropertyDeclaration,
- context: &mut computed::Context,
- );
-
-/// A per-longhand array of functions to perform the CSS cascade on each of
-/// them, effectively doing virtual dispatch.
-pub static CASCADE_PROPERTY: [CascadePropertyFn; ${len(data.longhands)}] = [
- % for property in data.longhands:
- longhands::${property.ident}::cascade_property,
- % endfor
-];
-
-
-/// See StyleAdjuster::adjust_for_border_width.
-pub fn adjust_border_width(style: &mut StyleBuilder) {
- % for side in ["top", "right", "bottom", "left"]:
- // Like calling to_computed_value, which wouldn't type check.
- if style.get_border().clone_border_${side}_style().none_or_hidden() &&
- style.get_border().border_${side}_has_nonzero_width() {
- style.set_border_${side}_width(Au(0));
- }
- % endfor
-}
-
-/// An identifier for a given alias property.
-#[derive(Clone, Copy, Eq, PartialEq, MallocSizeOf)]
-#[repr(u16)]
-pub enum AliasId {
- % for i, property in enumerate(data.all_aliases()):
- /// ${property.name}
- ${property.camel_case} = ${i},
- % endfor
-}
-
-#[derive(Clone, Copy, Eq, PartialEq)]
-enum AliasedPropertyId {
- #[allow(dead_code)] // Servo doesn't have aliased shorthands.
- Shorthand(ShorthandId),
- Longhand(LonghandId),
-}
-
-impl fmt::Debug for AliasId {
- fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
- let name = NonCustomPropertyId::from(*self).name();
- formatter.write_str(name)
- }
-}
-
-impl AliasId {
- /// Returns the property we're aliasing, as a longhand or a shorthand.
- #[inline]
- fn aliased_property(self) -> AliasedPropertyId {
- static MAP: [AliasedPropertyId; ${len(data.all_aliases())}] = [
- % for alias in data.all_aliases():
- % if alias.original.type() == "longhand":
- AliasedPropertyId::Longhand(LonghandId::${alias.original.camel_case}),
- % else:
- <% assert alias.original.type() == "shorthand" %>
- AliasedPropertyId::Shorthand(ShorthandId::${alias.original.camel_case}),
- % endif
- % endfor
- ];
- MAP[self as usize]
- }
-}
-
-/// Call the given macro with tokens like this for each longhand and shorthand properties
-/// that is enabled in content:
-///
-/// ```
-/// [CamelCaseName, SetCamelCaseName, PropertyId::Longhand(LonghandId::CamelCaseName)],
-/// ```
-///
-/// NOTE(emilio): Callers are responsible to deal with prefs.
-#[macro_export]
-macro_rules! css_properties_accessors {
- ($macro_name: ident) => {
- $macro_name! {
- % for kind, props in [("Longhand", data.longhands), ("Shorthand", data.shorthands)]:
- % for property in props:
- % if property.enabled_in_content():
- % for prop in [property] + property.aliases:
- % if '-' in prop.name:
- [${prop.ident.capitalize()}, Set${prop.ident.capitalize()},
- PropertyId::${kind}(${kind}Id::${property.camel_case})],
- % endif
- [${prop.camel_case}, Set${prop.camel_case},
- PropertyId::${kind}(${kind}Id::${property.camel_case})],
- % endfor
- % endif
- % endfor
- % endfor
- }
- }
-}
-
-/// Call the given macro with tokens like this for each longhand properties:
-///
-/// ```
-/// { snake_case_ident }
-/// ```
-///
-/// … where the boolean indicates whether the property value type
-/// is wrapped in a `Box<_>` in the corresponding `PropertyDeclaration` variant.
-#[macro_export]
-macro_rules! longhand_properties_idents {
- ($macro_name: ident) => {
- $macro_name! {
- % for property in data.longhands:
- { ${property.ident} }
- % endfor
- }
- }
-}
-
-// Large pages generate tens of thousands of ComputedValues.
-size_of_test!(ComputedValues, 192);
-// FFI relies on this.
-size_of_test!(Option<Arc<ComputedValues>>, 8);
-
-// There are two reasons for this test to fail:
-//
-// * Your changes made a specified value type for a given property go
-// over the threshold. In that case, you should try to shrink it again
-// or, if not possible, mark the property as boxed in the property
-// definition.
-//
-// * Your changes made a specified value type smaller, so that it no
-// longer needs to be boxed. In this case you just need to remove
-// boxed=True from the property definition. Nice job!
-#[cfg(target_pointer_width = "64")]
-#[allow(dead_code)] // https://github.com/rust-lang/rust/issues/96952
-const BOX_THRESHOLD: usize = 24;
-% for longhand in data.longhands:
-#[cfg(target_pointer_width = "64")]
-% if longhand.boxed:
-const_assert!(std::mem::size_of::<longhands::${longhand.ident}::SpecifiedValue>() > BOX_THRESHOLD);
-% else:
-const_assert!(std::mem::size_of::<longhands::${longhand.ident}::SpecifiedValue>() <= BOX_THRESHOLD);
-% endif
-% endfor
-
-% if engine == "servo":
-% for effect_name in ["repaint", "reflow_out_of_flow", "reflow", "rebuild_and_reflow_inline", "rebuild_and_reflow"]:
- macro_rules! restyle_damage_${effect_name} {
- ($old: ident, $new: ident, $damage: ident, [ $($effect:expr),* ]) => ({
- restyle_damage_${effect_name}!($old, $new, $damage, [$($effect),*], false)
- });
- ($old: ident, $new: ident, $damage: ident, [ $($effect:expr),* ], $extra:expr) => ({
- if
- % for style_struct in data.active_style_structs():
- % for longhand in style_struct.longhands:
- % if effect_name in longhand.servo_restyle_damage.split() and not longhand.logical:
- $old.get_${style_struct.name_lower}().${longhand.ident} !=
- $new.get_${style_struct.name_lower}().${longhand.ident} ||
- % endif
- % endfor
- % endfor
-
- $extra || false {
- $damage.insert($($effect)|*);
- true
- } else {
- false
- }
- });
- }
-% endfor
-% endif
diff --git a/components/style/properties/shorthands/background.mako.rs b/components/style/properties/shorthands/background.mako.rs
deleted file mode 100644
index 5fee5cb2b03..00000000000
--- a/components/style/properties/shorthands/background.mako.rs
+++ /dev/null
@@ -1,289 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
-
-<%namespace name="helpers" file="/helpers.mako.rs" />
-
-// TODO: other background-* properties
-<%helpers:shorthand name="background"
- engines="gecko servo"
- sub_properties="background-color background-position-x background-position-y background-repeat
- background-attachment background-image background-size background-origin
- background-clip"
- spec="https://drafts.csswg.org/css-backgrounds/#the-background">
- use crate::properties::longhands::{background_position_x, background_position_y, background_repeat};
- use crate::properties::longhands::{background_attachment, background_color, background_image, background_size, background_origin};
- use crate::properties::longhands::background_clip;
- use crate::properties::longhands::background_clip::single_value::computed_value::T as Clip;
- use crate::properties::longhands::background_origin::single_value::computed_value::T as Origin;
- use crate::values::specified::{AllowQuirks, Color, Position, PositionComponent};
- use crate::parser::Parse;
-
- // FIXME(emilio): Should be the same type!
- impl From<background_origin::single_value::SpecifiedValue> for background_clip::single_value::SpecifiedValue {
- fn from(origin: background_origin::single_value::SpecifiedValue) ->
- background_clip::single_value::SpecifiedValue {
- match origin {
- background_origin::single_value::SpecifiedValue::ContentBox =>
- background_clip::single_value::SpecifiedValue::ContentBox,
- background_origin::single_value::SpecifiedValue::PaddingBox =>
- background_clip::single_value::SpecifiedValue::PaddingBox,
- background_origin::single_value::SpecifiedValue::BorderBox =>
- background_clip::single_value::SpecifiedValue::BorderBox,
- }
- }
- }
-
- pub fn parse_value<'i, 't>(
- context: &ParserContext,
- input: &mut Parser<'i, 't>,
- ) -> Result<Longhands, ParseError<'i>> {
- let mut background_color = None;
-
- % for name in "image position_x position_y repeat size attachment origin clip".split():
- // Vec grows from 0 to 4 by default on first push(). So allocate with
- // capacity 1, so in the common case of only one item we don't way
- // overallocate, then shrink. Note that we always push at least one
- // item if parsing succeeds.
- let mut background_${name} = Vec::with_capacity(1);
- % endfor
- input.parse_comma_separated(|input| {
- // background-color can only be in the last element, so if it
- // is parsed anywhere before, the value is invalid.
- if background_color.is_some() {
- return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError));
- }
-
- % for name in "image position repeat size attachment origin clip".split():
- let mut ${name} = None;
- % endfor
- loop {
- if background_color.is_none() {
- if let Ok(value) = input.try_parse(|i| Color::parse(context, i)) {
- background_color = Some(value);
- continue
- }
- }
- if position.is_none() {
- if let Ok(value) = input.try_parse(|input| {
- Position::parse_three_value_quirky(context, input, AllowQuirks::No)
- }) {
- position = Some(value);
-
- // Parse background size, if applicable.
- size = input.try_parse(|input| {
- input.expect_delim('/')?;
- background_size::single_value::parse(context, input)
- }).ok();
-
- continue
- }
- }
- % for name in "image repeat attachment origin clip".split():
- if ${name}.is_none() {
- if let Ok(value) = input.try_parse(|input| background_${name}::single_value
- ::parse(context, input)) {
- ${name} = Some(value);
- continue
- }
- }
- % endfor
- break
- }
- if clip.is_none() {
- if let Some(origin) = origin {
- clip = Some(background_clip::single_value::SpecifiedValue::from(origin));
- }
- }
- let mut any = false;
- % for name in "image position repeat size attachment origin clip".split():
- any = any || ${name}.is_some();
- % endfor
- any = any || background_color.is_some();
- if any {
- if let Some(position) = position {
- background_position_x.push(position.horizontal);
- background_position_y.push(position.vertical);
- } else {
- background_position_x.push(PositionComponent::zero());
- background_position_y.push(PositionComponent::zero());
- }
- % for name in "image repeat size attachment origin clip".split():
- if let Some(bg_${name}) = ${name} {
- background_${name}.push(bg_${name});
- } else {
- background_${name}.push(background_${name}::single_value
- ::get_initial_specified_value());
- }
- % endfor
- Ok(())
- } else {
- Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError))
- }
- })?;
-
- Ok(expanded! {
- background_color: background_color.unwrap_or(Color::transparent()),
- % for name in "image position_x position_y repeat size attachment origin clip".split():
- background_${name}: background_${name}::SpecifiedValue(background_${name}.into()),
- % endfor
- })
- }
-
- impl<'a> ToCss for LonghandsToSerialize<'a> {
- fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result where W: fmt::Write {
- let len = self.background_image.0.len();
- // There should be at least one declared value
- if len == 0 {
- return Ok(());
- }
-
- // If a value list length is differs then we don't do a shorthand serialization.
- // The exceptions to this is color which appears once only and is serialized
- // with the last item.
- % for name in "image position_x position_y size repeat origin clip attachment".split():
- if len != self.background_${name}.0.len() {
- return Ok(());
- }
- % endfor
-
- for i in 0..len {
- % for name in "image position_x position_y repeat size attachment origin clip".split():
- let ${name} = &self.background_${name}.0[i];
- % endfor
-
- if i != 0 {
- dest.write_str(", ")?;
- }
-
- let mut wrote_value = false;
-
- if i == len - 1 {
- if *self.background_color != background_color::get_initial_specified_value() {
- self.background_color.to_css(dest)?;
- wrote_value = true;
- }
- }
-
- if *image != background_image::single_value::get_initial_specified_value() {
- if wrote_value {
- dest.write_char(' ')?;
- }
- image.to_css(dest)?;
- wrote_value = true;
- }
-
- // Size is only valid after a position so when there is a
- // non-initial size we must also serialize position
- if *position_x != PositionComponent::zero() ||
- *position_y != PositionComponent::zero() ||
- *size != background_size::single_value::get_initial_specified_value()
- {
- if wrote_value {
- dest.write_char(' ')?;
- }
-
- Position {
- horizontal: position_x.clone(),
- vertical: position_y.clone()
- }.to_css(dest)?;
-
- wrote_value = true;
-
- if *size != background_size::single_value::get_initial_specified_value() {
- dest.write_str(" / ")?;
- size.to_css(dest)?;
- }
- }
-
- % for name in "repeat attachment".split():
- if *${name} != background_${name}::single_value::get_initial_specified_value() {
- if wrote_value {
- dest.write_char(' ')?;
- }
- ${name}.to_css(dest)?;
- wrote_value = true;
- }
- % endfor
-
- if *origin != Origin::PaddingBox || *clip != Clip::BorderBox {
- if wrote_value {
- dest.write_char(' ')?;
- }
- origin.to_css(dest)?;
- if *clip != From::from(*origin) {
- dest.write_char(' ')?;
- clip.to_css(dest)?;
- }
-
- wrote_value = true;
- }
-
- if !wrote_value {
- image.to_css(dest)?;
- }
- }
-
- Ok(())
- }
- }
-</%helpers:shorthand>
-
-<%helpers:shorthand name="background-position"
- engines="gecko servo"
- flags="SHORTHAND_IN_GETCS"
- sub_properties="background-position-x background-position-y"
- spec="https://drafts.csswg.org/css-backgrounds-4/#the-background-position">
- use crate::properties::longhands::{background_position_x, background_position_y};
- use crate::values::specified::AllowQuirks;
- use crate::values::specified::position::Position;
-
- pub fn parse_value<'i, 't>(
- context: &ParserContext,
- input: &mut Parser<'i, 't>,
- ) -> Result<Longhands, ParseError<'i>> {
- // Vec grows from 0 to 4 by default on first push(). So allocate with
- // capacity 1, so in the common case of only one item we don't way
- // overallocate, then shrink. Note that we always push at least one
- // item if parsing succeeds.
- let mut position_x = Vec::with_capacity(1);
- let mut position_y = Vec::with_capacity(1);
- let mut any = false;
-
- input.parse_comma_separated(|input| {
- let value = Position::parse_three_value_quirky(context, input, AllowQuirks::Yes)?;
- position_x.push(value.horizontal);
- position_y.push(value.vertical);
- any = true;
- Ok(())
- })?;
- if !any {
- return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError));
- }
-
- Ok(expanded! {
- background_position_x: background_position_x::SpecifiedValue(position_x.into()),
- background_position_y: background_position_y::SpecifiedValue(position_y.into()),
- })
- }
-
- impl<'a> ToCss for LonghandsToSerialize<'a> {
- fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result where W: fmt::Write {
- let len = self.background_position_x.0.len();
- if len == 0 || len != self.background_position_y.0.len() {
- return Ok(());
- }
- for i in 0..len {
- Position {
- horizontal: self.background_position_x.0[i].clone(),
- vertical: self.background_position_y.0[i].clone()
- }.to_css(dest)?;
-
- if i < len - 1 {
- dest.write_str(", ")?;
- }
- }
- Ok(())
- }
- }
-</%helpers:shorthand>
diff --git a/components/style/properties/shorthands/border.mako.rs b/components/style/properties/shorthands/border.mako.rs
deleted file mode 100644
index 2eeb691e242..00000000000
--- a/components/style/properties/shorthands/border.mako.rs
+++ /dev/null
@@ -1,492 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
-
-<%namespace name="helpers" file="/helpers.mako.rs" />
-<% from data import to_rust_ident, ALL_SIDES, PHYSICAL_SIDES, maybe_moz_logical_alias %>
-
-${helpers.four_sides_shorthand(
- "border-color",
- "border-%s-color",
- "specified::Color::parse",
- engines="gecko servo",
- spec="https://drafts.csswg.org/css-backgrounds/#border-color",
- allow_quirks="Yes",
-)}
-
-${helpers.four_sides_shorthand(
- "border-style",
- "border-%s-style",
- engines="gecko servo",
- spec="https://drafts.csswg.org/css-backgrounds/#border-style",
-)}
-
-<%helpers:shorthand
- name="border-width"
- engines="gecko servo"
- sub_properties="${
- ' '.join('border-%s-width' % side
- for side in PHYSICAL_SIDES)}"
- spec="https://drafts.csswg.org/css-backgrounds/#border-width">
- use crate::values::generics::rect::Rect;
- use crate::values::specified::{AllowQuirks, BorderSideWidth};
-
- pub fn parse_value<'i, 't>(
- context: &ParserContext,
- input: &mut Parser<'i, 't>,
- ) -> Result<Longhands, ParseError<'i>> {
- let rect = Rect::parse_with(context, input, |_, i| {
- BorderSideWidth::parse_quirky(context, i, AllowQuirks::Yes)
- })?;
- Ok(expanded! {
- border_top_width: rect.0,
- border_right_width: rect.1,
- border_bottom_width: rect.2,
- border_left_width: rect.3,
- })
- }
-
- impl<'a> ToCss for LonghandsToSerialize<'a> {
- fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result where W: fmt::Write {
- % for side in PHYSICAL_SIDES:
- let ${side} = &self.border_${side}_width;
- % endfor
- Rect::new(top, right, bottom, left).to_css(dest)
- }
- }
-</%helpers:shorthand>
-
-
-pub fn parse_border<'i, 't>(
- context: &ParserContext,
- input: &mut Parser<'i, 't>,
-) -> Result<(specified::Color, specified::BorderStyle, specified::BorderSideWidth), ParseError<'i>> {
- use crate::values::specified::{Color, BorderStyle, BorderSideWidth};
- let _unused = context;
- let mut color = None;
- let mut style = None;
- let mut width = None;
- let mut any = false;
- loop {
- if width.is_none() {
- if let Ok(value) = input.try_parse(|i| BorderSideWidth::parse(context, i)) {
- width = Some(value);
- any = true;
- }
- }
- if style.is_none() {
- if let Ok(value) = input.try_parse(BorderStyle::parse) {
- style = Some(value);
- any = true;
- continue
- }
- }
- if color.is_none() {
- if let Ok(value) = input.try_parse(|i| Color::parse(context, i)) {
- color = Some(value);
- any = true;
- continue
- }
- }
- break
- }
- if !any {
- return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError))
- }
- Ok((color.unwrap_or(Color::CurrentColor), style.unwrap_or(BorderStyle::None), width.unwrap_or(BorderSideWidth::medium())))
-}
-
-% for side, logical in ALL_SIDES:
- <%
- spec = "https://drafts.csswg.org/css-backgrounds/#border-%s" % side
- if logical:
- spec = "https://drafts.csswg.org/css-logical-props/#propdef-border-%s" % side
- %>
- <%helpers:shorthand
- name="border-${side}"
- engines="gecko servo"
- sub_properties="${' '.join(
- 'border-%s-%s' % (side, prop)
- for prop in ['width', 'style', 'color']
- )}"
- aliases="${maybe_moz_logical_alias(engine, (side, logical), '-moz-border-%s')}"
- spec="${spec}">
-
- pub fn parse_value<'i, 't>(
- context: &ParserContext,
- input: &mut Parser<'i, 't>,
- ) -> Result<Longhands, ParseError<'i>> {
- let (color, style, width) = super::parse_border(context, input)?;
- Ok(expanded! {
- border_${to_rust_ident(side)}_color: color,
- border_${to_rust_ident(side)}_style: style,
- border_${to_rust_ident(side)}_width: width
- })
- }
-
- impl<'a> ToCss for LonghandsToSerialize<'a> {
- fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result where W: fmt::Write {
- crate::values::specified::border::serialize_directional_border(
- dest,
- self.border_${to_rust_ident(side)}_width,
- self.border_${to_rust_ident(side)}_style,
- self.border_${to_rust_ident(side)}_color
- )
- }
- }
-
- </%helpers:shorthand>
-% endfor
-
-<%helpers:shorthand name="border"
- engines="gecko servo"
- sub_properties="${' '.join('border-%s-%s' % (side, prop)
- for side in PHYSICAL_SIDES for prop in ['width', 'style', 'color']
- )}
- ${' '.join('border-image-%s' % name
- for name in ['outset', 'repeat', 'slice', 'source', 'width'])}"
- derive_value_info="False"
- spec="https://drafts.csswg.org/css-backgrounds/#border">
-
- pub fn parse_value<'i, 't>(
- context: &ParserContext,
- input: &mut Parser<'i, 't>,
- ) -> Result<Longhands, ParseError<'i>> {
- use crate::properties::longhands::{border_image_outset, border_image_repeat, border_image_slice};
- use crate::properties::longhands::{border_image_source, border_image_width};
-
- let (color, style, width) = super::parse_border(context, input)?;
- Ok(expanded! {
- % for side in PHYSICAL_SIDES:
- border_${side}_color: color.clone(),
- border_${side}_style: style,
- border_${side}_width: width.clone(),
- % endfor
-
- // The ‘border’ shorthand resets ‘border-image’ to its initial value.
- // See https://drafts.csswg.org/css-backgrounds-3/#the-border-shorthands
- % for name in "outset repeat slice source width".split():
- border_image_${name}: border_image_${name}::get_initial_specified_value(),
- % endfor
- })
- }
-
- impl<'a> ToCss for LonghandsToSerialize<'a> {
- fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result where W: fmt::Write {
- use crate::properties::longhands;
-
- // If any of the border-image longhands differ from their initial specified values we should not
- // invoke serialize_directional_border(), so there is no point in continuing on to compute all_equal.
- % for name in "outset repeat slice source width".split():
- if *self.border_image_${name} != longhands::border_image_${name}::get_initial_specified_value() {
- return Ok(());
- }
- % endfor
-
- let all_equal = {
- % for side in PHYSICAL_SIDES:
- let border_${side}_width = self.border_${side}_width;
- let border_${side}_style = self.border_${side}_style;
- let border_${side}_color = self.border_${side}_color;
- % endfor
-
- border_top_width == border_right_width &&
- border_right_width == border_bottom_width &&
- border_bottom_width == border_left_width &&
-
- border_top_style == border_right_style &&
- border_right_style == border_bottom_style &&
- border_bottom_style == border_left_style &&
-
- border_top_color == border_right_color &&
- border_right_color == border_bottom_color &&
- border_bottom_color == border_left_color
- };
-
- // If all longhands are all present, then all sides should be the same,
- // so we can just one set of color/style/width
- if !all_equal {
- return Ok(())
- }
- crate::values::specified::border::serialize_directional_border(
- dest,
- self.border_${side}_width,
- self.border_${side}_style,
- self.border_${side}_color
- )
- }
- }
-
- // Just use the same as border-left. The border shorthand can't accept
- // any value that the sub-shorthand couldn't.
- <%
- border_left = "<crate::properties::shorthands::border_left::Longhands as SpecifiedValueInfo>"
- %>
- impl SpecifiedValueInfo for Longhands {
- const SUPPORTED_TYPES: u8 = ${border_left}::SUPPORTED_TYPES;
- fn collect_completion_keywords(f: KeywordsCollectFn) {
- ${border_left}::collect_completion_keywords(f);
- }
- }
-</%helpers:shorthand>
-
-<%helpers:shorthand
- name="border-radius"
- engines="gecko servo"
- sub_properties="${' '.join(
- 'border-%s-radius' % (corner)
- for corner in ['top-left', 'top-right', 'bottom-right', 'bottom-left']
- )}"
- extra_prefixes="webkit"
- spec="https://drafts.csswg.org/css-backgrounds/#border-radius"
->
- use crate::values::generics::rect::Rect;
- use crate::values::generics::border::BorderCornerRadius;
- use crate::values::specified::border::BorderRadius;
- use crate::parser::Parse;
-
- pub fn parse_value<'i, 't>(
- context: &ParserContext,
- input: &mut Parser<'i, 't>,
- ) -> Result<Longhands, ParseError<'i>> {
- let radii = BorderRadius::parse(context, input)?;
- Ok(expanded! {
- border_top_left_radius: radii.top_left,
- border_top_right_radius: radii.top_right,
- border_bottom_right_radius: radii.bottom_right,
- border_bottom_left_radius: radii.bottom_left,
- })
- }
-
- impl<'a> ToCss for LonghandsToSerialize<'a> {
- fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result where W: fmt::Write {
- let LonghandsToSerialize {
- border_top_left_radius: &BorderCornerRadius(ref tl),
- border_top_right_radius: &BorderCornerRadius(ref tr),
- border_bottom_right_radius: &BorderCornerRadius(ref br),
- border_bottom_left_radius: &BorderCornerRadius(ref bl),
- } = *self;
-
-
- let widths = Rect::new(tl.width(), tr.width(), br.width(), bl.width());
- let heights = Rect::new(tl.height(), tr.height(), br.height(), bl.height());
-
- BorderRadius::serialize_rects(widths, heights, dest)
- }
- }
-</%helpers:shorthand>
-
-<%helpers:shorthand
- name="border-image"
- engines="gecko servo"
- servo_pref="layout.legacy_layout",
- sub_properties="border-image-outset
- border-image-repeat border-image-slice border-image-source border-image-width"
- extra_prefixes="moz:layout.css.prefixes.border-image webkit"
- spec="https://drafts.csswg.org/css-backgrounds-3/#border-image"
->
- use crate::properties::longhands::{border_image_outset, border_image_repeat, border_image_slice};
- use crate::properties::longhands::{border_image_source, border_image_width};
-
- pub fn parse_value<'i, 't>(
- context: &ParserContext,
- input: &mut Parser<'i, 't>,
- ) -> Result<Longhands, ParseError<'i>> {
- % for name in "outset repeat slice source width".split():
- let mut ${name} = border_image_${name}::get_initial_specified_value();
- % endfor
- let mut any = false;
- let mut parsed_slice = false;
- let mut parsed_source = false;
- let mut parsed_repeat = false;
- loop {
- if !parsed_slice {
- if let Ok(value) = input.try_parse(|input| border_image_slice::parse(context, input)) {
- parsed_slice = true;
- any = true;
- slice = value;
- // Parse border image width and outset, if applicable.
- let maybe_width_outset: Result<_, ParseError> = input.try_parse(|input| {
- input.expect_delim('/')?;
-
- // Parse border image width, if applicable.
- let w = input.try_parse(|input| border_image_width::parse(context, input)).ok();
-
- // Parse border image outset if applicable.
- let o = input.try_parse(|input| {
- input.expect_delim('/')?;
- border_image_outset::parse(context, input)
- }).ok();
- if w.is_none() && o.is_none() {
- return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError))
- }
- Ok((w, o))
- });
- if let Ok((w, o)) = maybe_width_outset {
- if let Some(w) = w {
- width = w;
- }
- if let Some(o) = o {
- outset = o;
- }
- }
- continue;
- }
- }
- % for name in "source repeat".split():
- if !parsed_${name} {
- if let Ok(value) = input.try_parse(|input| border_image_${name}::parse(context, input)) {
- ${name} = value;
- parsed_${name} = true;
- any = true;
- continue
- }
- }
- % endfor
- break
- }
- if !any {
- return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError))
- }
- Ok(expanded! {
- % for name in "outset repeat slice source width".split():
- border_image_${name}: ${name},
- % endfor
- })
- }
-
- impl<'a> ToCss for LonghandsToSerialize<'a> {
- fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result where W: fmt::Write {
- let mut has_any = false;
- % for name in "source slice outset width repeat".split():
- let has_${name} = *self.border_image_${name} != border_image_${name}::get_initial_specified_value();
- has_any = has_any || has_${name};
- % endfor
- if has_source || !has_any {
- self.border_image_source.to_css(dest)?;
- if !has_any {
- return Ok(());
- }
- }
- let needs_slice = has_slice || has_width || has_outset;
- if needs_slice {
- if has_source {
- dest.write_char(' ')?;
- }
- self.border_image_slice.to_css(dest)?;
- if has_width || has_outset {
- dest.write_str(" /")?;
- if has_width {
- dest.write_char(' ')?;
- self.border_image_width.to_css(dest)?;
- }
- if has_outset {
- dest.write_str(" / ")?;
- self.border_image_outset.to_css(dest)?;
- }
- }
- }
- if has_repeat {
- if has_source || needs_slice {
- dest.write_char(' ')?;
- }
- self.border_image_repeat.to_css(dest)?;
- }
- Ok(())
- }
- }
-</%helpers:shorthand>
-
-% for axis in ["block", "inline"]:
- % for prop in ["width", "style", "color"]:
- <%
- spec = "https://drafts.csswg.org/css-logical/#propdef-border-%s-%s" % (axis, prop)
- %>
- <%helpers:shorthand
- engines="gecko servo"
- name="border-${axis}-${prop}"
- sub_properties="${' '.join(
- 'border-%s-%s-%s' % (axis, side, prop)
- for side in ['start', 'end']
- )}"
- spec="${spec}">
-
- use crate::properties::longhands::border_${axis}_start_${prop};
- pub fn parse_value<'i, 't>(
- context: &ParserContext,
- input: &mut Parser<'i, 't>,
- ) -> Result<Longhands, ParseError<'i>> {
- let start_value = border_${axis}_start_${prop}::parse(context, input)?;
- let end_value =
- input.try_parse(|input| border_${axis}_start_${prop}::parse(context, input))
- .unwrap_or_else(|_| start_value.clone());
-
- Ok(expanded! {
- border_${axis}_start_${prop}: start_value,
- border_${axis}_end_${prop}: end_value,
- })
- }
-
- impl<'a> ToCss for LonghandsToSerialize<'a> {
- fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result where W: fmt::Write {
- self.border_${axis}_start_${prop}.to_css(dest)?;
-
- if self.border_${axis}_end_${prop} != self.border_${axis}_start_${prop} {
- dest.write_char(' ')?;
- self.border_${axis}_end_${prop}.to_css(dest)?;
- }
-
- Ok(())
- }
- }
- </%helpers:shorthand>
- % endfor
-% endfor
-
-% for axis in ["block", "inline"]:
- <%
- spec = "https://drafts.csswg.org/css-logical/#propdef-border-%s" % (axis)
- %>
- <%helpers:shorthand
- name="border-${axis}"
- engines="gecko servo"
- sub_properties="${' '.join(
- 'border-%s-%s-width' % (axis, side)
- for side in ['start', 'end']
- )} ${' '.join(
- 'border-%s-%s-style' % (axis, side)
- for side in ['start', 'end']
- )} ${' '.join(
- 'border-%s-%s-color' % (axis, side)
- for side in ['start', 'end']
- )}"
- spec="${spec}">
-
- use crate::properties::shorthands::border_${axis}_start;
- pub fn parse_value<'i, 't>(
- context: &ParserContext,
- input: &mut Parser<'i, 't>,
- ) -> Result<Longhands, ParseError<'i>> {
- let start_value = border_${axis}_start::parse_value(context, input)?;
- Ok(expanded! {
- border_${axis}_start_width: start_value.border_${axis}_start_width.clone(),
- border_${axis}_end_width: start_value.border_${axis}_start_width,
- border_${axis}_start_style: start_value.border_${axis}_start_style.clone(),
- border_${axis}_end_style: start_value.border_${axis}_start_style,
- border_${axis}_start_color: start_value.border_${axis}_start_color.clone(),
- border_${axis}_end_color: start_value.border_${axis}_start_color,
- })
- }
-
- impl<'a> ToCss for LonghandsToSerialize<'a> {
- fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result where W: fmt::Write {
- crate::values::specified::border::serialize_directional_border(
- dest,
- self.border_${axis}_start_width,
- self.border_${axis}_start_style,
- self.border_${axis}_start_color
- )
- }
- }
- </%helpers:shorthand>
-% endfor
diff --git a/components/style/properties/shorthands/box.mako.rs b/components/style/properties/shorthands/box.mako.rs
deleted file mode 100644
index b9885492cd8..00000000000
--- a/components/style/properties/shorthands/box.mako.rs
+++ /dev/null
@@ -1,311 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
-
-<%namespace name="helpers" file="/helpers.mako.rs" />
-
-${helpers.two_properties_shorthand(
- "overflow",
- "overflow-x",
- "overflow-y",
- engines="gecko servo",
- flags="SHORTHAND_IN_GETCS",
- spec="https://drafts.csswg.org/css-overflow/#propdef-overflow",
-)}
-
-${helpers.two_properties_shorthand(
- "overflow-clip-box",
- "overflow-clip-box-block",
- "overflow-clip-box-inline",
- engines="gecko",
- enabled_in="ua",
- gecko_pref="layout.css.overflow-clip-box.enabled",
- spec="Internal, may be standardized in the future "
- "(https://developer.mozilla.org/en-US/docs/Web/CSS/overflow-clip-box)",
-)}
-
-${helpers.two_properties_shorthand(
- "overscroll-behavior",
- "overscroll-behavior-x",
- "overscroll-behavior-y",
- engines="gecko",
- gecko_pref="layout.css.overscroll-behavior.enabled",
- spec="https://wicg.github.io/overscroll-behavior/#overscroll-behavior-properties",
-)}
-
-<%helpers:shorthand
- engines="gecko"
- name="container"
- sub_properties="container-name container-type"
- gecko_pref="layout.css.container-queries.enabled"
- enabled_in="ua"
- spec="https://drafts.csswg.org/css-contain-3/#container-shorthand"
->
- use crate::values::specified::box_::{ContainerName, ContainerType};
- pub fn parse_value<'i>(
- context: &ParserContext,
- input: &mut Parser<'i, '_>,
- ) -> Result<Longhands, ParseError<'i>> {
- use crate::parser::Parse;
- // See https://github.com/w3c/csswg-drafts/issues/7180 for why we don't
- // match the spec.
- let container_name = ContainerName::parse(context, input)?;
- let container_type = if input.try_parse(|input| input.expect_delim('/')).is_ok() {
- ContainerType::parse(input)?
- } else {
- ContainerType::Normal
- };
- Ok(expanded! {
- container_name: container_name,
- container_type: container_type,
- })
- }
-
- impl<'a> ToCss for LonghandsToSerialize<'a> {
- fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result where W: fmt::Write {
- self.container_name.to_css(dest)?;
- if !self.container_type.is_normal() {
- dest.write_str(" / ")?;
- self.container_type.to_css(dest)?;
- }
- Ok(())
- }
- }
-</%helpers:shorthand>
-
-<%helpers:shorthand
- engines="gecko"
- name="page-break-before"
- flags="SHORTHAND_IN_GETCS IS_LEGACY_SHORTHAND"
- sub_properties="break-before"
- spec="https://drafts.csswg.org/css-break-3/#page-break-properties"
->
- pub fn parse_value<'i>(
- context: &ParserContext,
- input: &mut Parser<'i, '_>,
- ) -> Result<Longhands, ParseError<'i>> {
- use crate::values::specified::box_::BreakBetween;
- Ok(expanded! {
- break_before: BreakBetween::parse_legacy(context, input)?,
- })
- }
-
- impl<'a> ToCss for LonghandsToSerialize<'a> {
- fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result where W: fmt::Write {
- self.break_before.to_css_legacy(dest)
- }
- }
-</%helpers:shorthand>
-
-<%helpers:shorthand
- engines="gecko"
- name="page-break-after"
- flags="SHORTHAND_IN_GETCS IS_LEGACY_SHORTHAND"
- sub_properties="break-after"
- spec="https://drafts.csswg.org/css-break-3/#page-break-properties"
->
- pub fn parse_value<'i>(
- context: &ParserContext,
- input: &mut Parser<'i, '_>,
- ) -> Result<Longhands, ParseError<'i>> {
- use crate::values::specified::box_::BreakBetween;
- Ok(expanded! {
- break_after: BreakBetween::parse_legacy(context, input)?,
- })
- }
-
- impl<'a> ToCss for LonghandsToSerialize<'a> {
- fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result where W: fmt::Write {
- self.break_after.to_css_legacy(dest)
- }
- }
-</%helpers:shorthand>
-
-<%helpers:shorthand
- engines="gecko"
- name="page-break-inside"
- flags="SHORTHAND_IN_GETCS IS_LEGACY_SHORTHAND"
- sub_properties="break-inside"
- spec="https://drafts.csswg.org/css-break-3/#page-break-properties"
->
- pub fn parse_value<'i>(
- context: &ParserContext,
- input: &mut Parser<'i, '_>,
- ) -> Result<Longhands, ParseError<'i>> {
- use crate::values::specified::box_::BreakWithin;
- Ok(expanded! {
- break_inside: BreakWithin::parse_legacy(context, input)?,
- })
- }
-
- impl<'a> ToCss for LonghandsToSerialize<'a> {
- fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result where W: fmt::Write {
- self.break_inside.to_css_legacy(dest)
- }
- }
-</%helpers:shorthand>
-
-<%helpers:shorthand name="offset"
- engines="gecko"
- sub_properties="offset-path offset-distance offset-rotate offset-anchor
- offset-position"
- gecko_pref="layout.css.motion-path.enabled",
- spec="https://drafts.fxtf.org/motion-1/#offset-shorthand">
- use crate::parser::Parse;
- use crate::values::specified::motion::{OffsetPath, OffsetPosition, OffsetRotate};
- use crate::values::specified::{LengthPercentage, PositionOrAuto};
- use crate::Zero;
-
- pub fn parse_value<'i, 't>(
- context: &ParserContext,
- input: &mut Parser<'i, 't>,
- ) -> Result<Longhands, ParseError<'i>> {
- let offset_position =
- if static_prefs::pref!("layout.css.motion-path-offset-position.enabled") {
- input.try_parse(|i| OffsetPosition::parse(context, i)).ok()
- } else {
- None
- };
-
- let offset_path = input.try_parse(|i| OffsetPath::parse(context, i)).ok();
-
- // Must have one of [offset-position, offset-path].
- // FIXME: The syntax is out-of-date after the update of the spec.
- // https://github.com/w3c/fxtf-drafts/issues/515
- if offset_position.is_none() && offset_path.is_none() {
- return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError));
- }
-
- let mut offset_distance = None;
- let mut offset_rotate = None;
- // offset-distance and offset-rotate are grouped with offset-path.
- if offset_path.is_some() {
- loop {
- if offset_distance.is_none() {
- if let Ok(value) = input.try_parse(|i| LengthPercentage::parse(context, i)) {
- offset_distance = Some(value);
- }
- }
-
- if offset_rotate.is_none() {
- if let Ok(value) = input.try_parse(|i| OffsetRotate::parse(context, i)) {
- offset_rotate = Some(value);
- continue;
- }
- }
- break;
- }
- }
-
- let offset_anchor = input.try_parse(|i| {
- i.expect_delim('/')?;
- PositionOrAuto::parse(context, i)
- }).ok();
-
- Ok(expanded! {
- offset_position: offset_position.unwrap_or(OffsetPosition::auto()),
- offset_path: offset_path.unwrap_or(OffsetPath::none()),
- offset_distance: offset_distance.unwrap_or(LengthPercentage::zero()),
- offset_rotate: offset_rotate.unwrap_or(OffsetRotate::auto()),
- offset_anchor: offset_anchor.unwrap_or(PositionOrAuto::auto()),
- })
- }
-
- impl<'a> ToCss for LonghandsToSerialize<'a> {
- fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result where W: fmt::Write {
- if let Some(offset_position) = self.offset_position {
- // The basic concept is: we must serialize offset-position or offset-path group.
- // offset-path group means "offset-path offset-distance offset-rotate".
- let must_serialize_path = *self.offset_path != OffsetPath::None
- || (!self.offset_distance.is_zero() || !self.offset_rotate.is_auto());
- let position_is_default = matches!(offset_position, OffsetPosition::Auto);
- if !position_is_default || !must_serialize_path {
- offset_position.to_css(dest)?;
- }
-
- if must_serialize_path {
- if !position_is_default {
- dest.write_char(' ')?;
- }
- self.offset_path.to_css(dest)?;
- }
- } else {
- // If the pref is off, we always show offset-path.
- self.offset_path.to_css(dest)?;
- }
-
- if !self.offset_distance.is_zero() {
- dest.write_char(' ')?;
- self.offset_distance.to_css(dest)?;
- }
-
- if !self.offset_rotate.is_auto() {
- dest.write_char(' ')?;
- self.offset_rotate.to_css(dest)?;
- }
-
- if *self.offset_anchor != PositionOrAuto::auto() {
- dest.write_str(" / ")?;
- self.offset_anchor.to_css(dest)?;
- }
- Ok(())
- }
- }
-</%helpers:shorthand>
-
-<%helpers:shorthand name="zoom" engines="gecko"
- sub_properties="transform transform-origin"
- gecko_pref="layout.css.zoom-transform-hack.enabled"
- flags="SHORTHAND_IN_GETCS IS_LEGACY_SHORTHAND"
- spec="Not a standard, only a compat hack">
- use crate::parser::Parse;
- use crate::values::specified::{Number, NumberOrPercentage, TransformOrigin};
- use crate::values::generics::transform::{Transform, TransformOperation};
-
- pub fn parse_value<'i, 't>(
- context: &ParserContext,
- input: &mut Parser<'i, 't>,
- ) -> Result<Longhands, ParseError<'i>> {
- let zoom = match input.try_parse(|input| NumberOrPercentage::parse(context, input)) {
- Ok(number_or_percent) => number_or_percent.to_number(),
- Err(..) => {
- input.expect_ident_matching("normal")?;
- Number::new(1.0)
- },
- };
-
- // Make sure that the initial value matches the values for the
- // longhands, just for general sanity. `zoom: 1` and `zoom: 0` are
- // ignored, see [1][2]. They are just hack for the "has layout" mode on
- // IE.
- //
- // [1]: https://bugs.webkit.org/show_bug.cgi?id=18467
- // [2]: https://bugzilla.mozilla.org/show_bug.cgi?id=1593009
- Ok(if zoom.get() == 1.0 || zoom.get() == 0.0 {
- expanded! {
- transform: Transform::none(),
- transform_origin: TransformOrigin::initial_value(),
- }
- } else {
- expanded! {
- transform: Transform(vec![TransformOperation::Scale(zoom, zoom)].into()),
- transform_origin: TransformOrigin::zero_zero(),
- }
- })
- }
-
- impl<'a> ToCss for LonghandsToSerialize<'a> {
- fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result where W: fmt::Write {
- if self.transform.0.is_empty() && *self.transform_origin == TransformOrigin::initial_value() {
- return 1.0f32.to_css(dest);
- }
- if *self.transform_origin != TransformOrigin::zero_zero() {
- return Ok(())
- }
- match &*self.transform.0 {
- [TransformOperation::Scale(x, y)] if x == y => x.to_css(dest),
- _ => Ok(()),
- }
- }
- }
-</%helpers:shorthand>
diff --git a/components/style/properties/shorthands/column.mako.rs b/components/style/properties/shorthands/column.mako.rs
deleted file mode 100644
index 4cf9a8d7866..00000000000
--- a/components/style/properties/shorthands/column.mako.rs
+++ /dev/null
@@ -1,115 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
-
-<%namespace name="helpers" file="/helpers.mako.rs" />
-
-<%helpers:shorthand name="columns"
- engines="gecko servo"
- sub_properties="column-width column-count"
- servo_pref="layout.columns.enabled"
- spec="https://drafts.csswg.org/css-multicol/#propdef-columns">
- use crate::properties::longhands::{column_count, column_width};
-
- pub fn parse_value<'i, 't>(
- context: &ParserContext,
- input: &mut Parser<'i, 't>,
- ) -> Result<Longhands, ParseError<'i>> {
- let mut column_count = None;
- let mut column_width = None;
- let mut autos = 0;
-
- loop {
- if input.try_parse(|input| input.expect_ident_matching("auto")).is_ok() {
- // Leave the options to None, 'auto' is the initial value.
- autos += 1;
- continue
- }
-
- if column_count.is_none() {
- if let Ok(value) = input.try_parse(|input| column_count::parse(context, input)) {
- column_count = Some(value);
- continue
- }
- }
-
- if column_width.is_none() {
- if let Ok(value) = input.try_parse(|input| column_width::parse(context, input)) {
- column_width = Some(value);
- continue
- }
- }
-
- break
- }
-
- let values = autos + column_count.iter().len() + column_width.iter().len();
- if values == 0 || values > 2 {
- Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError))
- } else {
- Ok(expanded! {
- column_count: unwrap_or_initial!(column_count),
- column_width: unwrap_or_initial!(column_width),
- })
- }
- }
-
- impl<'a> ToCss for LonghandsToSerialize<'a> {
- fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result where W: fmt::Write {
- if self.column_width.is_auto() {
- return self.column_count.to_css(dest)
- }
- self.column_width.to_css(dest)?;
- if !self.column_count.is_auto() {
- dest.write_char(' ')?;
- self.column_count.to_css(dest)?;
- }
- Ok(())
- }
- }
-</%helpers:shorthand>
-
-<%helpers:shorthand
- name="column-rule"
- engines="gecko"
- sub_properties="column-rule-width column-rule-style column-rule-color"
- derive_serialize="True"
- spec="https://drafts.csswg.org/css-multicol/#propdef-column-rule"
->
- use crate::properties::longhands::{column_rule_width, column_rule_style};
- use crate::properties::longhands::column_rule_color;
-
- pub fn parse_value<'i, 't>(
- context: &ParserContext,
- input: &mut Parser<'i, 't>,
- ) -> Result<Longhands, ParseError<'i>> {
- % for name in "width style color".split():
- let mut column_rule_${name} = None;
- % endfor
- let mut any = false;
-
- loop {
- % for name in "width style color".split():
- if column_rule_${name}.is_none() {
- if let Ok(value) = input.try_parse(|input|
- column_rule_${name}::parse(context, input)) {
- column_rule_${name} = Some(value);
- any = true;
- continue
- }
- }
- % endfor
-
- break
- }
- if any {
- Ok(expanded! {
- column_rule_width: unwrap_or_initial!(column_rule_width),
- column_rule_style: unwrap_or_initial!(column_rule_style),
- column_rule_color: unwrap_or_initial!(column_rule_color),
- })
- } else {
- Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError))
- }
- }
-</%helpers:shorthand>
diff --git a/components/style/properties/shorthands/font.mako.rs b/components/style/properties/shorthands/font.mako.rs
deleted file mode 100644
index ec682048e75..00000000000
--- a/components/style/properties/shorthands/font.mako.rs
+++ /dev/null
@@ -1,547 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
-
-<%namespace name="helpers" file="/helpers.mako.rs" />
-<% from data import SYSTEM_FONT_LONGHANDS %>
-
-<%helpers:shorthand
- name="font"
- engines="gecko servo"
- sub_properties="
- font-style
- font-variant-caps
- font-weight
- font-stretch
- font-size
- line-height
- font-family
- ${'font-size-adjust' if engine == 'gecko' else ''}
- ${'font-kerning' if engine == 'gecko' else ''}
- ${'font-optical-sizing' if engine == 'gecko' else ''}
- ${'font-variant-alternates' if engine == 'gecko' else ''}
- ${'font-variant-east-asian' if engine == 'gecko' else ''}
- ${'font-variant-emoji' if engine == 'gecko' else ''}
- ${'font-variant-ligatures' if engine == 'gecko' else ''}
- ${'font-variant-numeric' if engine == 'gecko' else ''}
- ${'font-variant-position' if engine == 'gecko' else ''}
- ${'font-language-override' if engine == 'gecko' else ''}
- ${'font-feature-settings' if engine == 'gecko' else ''}
- ${'font-variation-settings' if engine == 'gecko' else ''}
- "
- derive_value_info="False"
- spec="https://drafts.csswg.org/css-fonts-3/#propdef-font"
->
- use crate::computed_values::font_variant_caps::T::SmallCaps;
- use crate::parser::Parse;
- use crate::properties::longhands::{font_family, font_style, font_weight, font_stretch};
- #[cfg(feature = "gecko")]
- use crate::properties::longhands::font_size;
- use crate::properties::longhands::font_variant_caps;
- use crate::values::specified::text::LineHeight;
- use crate::values::specified::{FontSize, FontWeight};
- use crate::values::specified::font::{FontStretch, FontStretchKeyword};
- #[cfg(feature = "gecko")]
- use crate::values::specified::font::SystemFont;
-
- <%
- gecko_sub_properties = "kerning language_override size_adjust \
- variant_alternates variant_east_asian \
- variant_emoji variant_ligatures \
- variant_numeric variant_position \
- feature_settings variation_settings \
- optical_sizing".split()
- %>
- % if engine == "gecko":
- % for prop in gecko_sub_properties:
- use crate::properties::longhands::font_${prop};
- % endfor
- % endif
- use self::font_family::SpecifiedValue as FontFamily;
-
- pub fn parse_value<'i, 't>(
- context: &ParserContext,
- input: &mut Parser<'i, 't>,
- ) -> Result<Longhands, ParseError<'i>> {
- let mut nb_normals = 0;
- let mut style = None;
- let mut variant_caps = None;
- let mut weight = None;
- let mut stretch = None;
- let size;
- % if engine == "gecko":
- if let Ok(sys) = input.try_parse(|i| SystemFont::parse(context, i)) {
- return Ok(expanded! {
- % for name in SYSTEM_FONT_LONGHANDS:
- ${name}: ${name}::SpecifiedValue::system_font(sys),
- % endfor
- line_height: LineHeight::normal(),
- % for name in gecko_sub_properties + ["variant_caps"]:
- font_${name}: font_${name}::get_initial_specified_value(),
- % endfor
- })
- }
- % endif
- loop {
- // Special-case 'normal' because it is valid in each of
- // font-style, font-weight, font-variant and font-stretch.
- // Leaves the values to None, 'normal' is the initial value for each of them.
- if input.try_parse(|input| input.expect_ident_matching("normal")).is_ok() {
- nb_normals += 1;
- continue;
- }
- if style.is_none() {
- if let Ok(value) = input.try_parse(|input| font_style::parse(context, input)) {
- style = Some(value);
- continue
- }
- }
- if weight.is_none() {
- if let Ok(value) = input.try_parse(|input| font_weight::parse(context, input)) {
- weight = Some(value);
- continue
- }
- }
- if variant_caps.is_none() {
- // The only variant-caps value allowed is small-caps (from CSS2); the added values
- // defined by CSS Fonts 3 and later are not accepted.
- // https://www.w3.org/TR/css-fonts-4/#font-prop
- if input.try_parse(|input| input.expect_ident_matching("small-caps")).is_ok() {
- variant_caps = Some(SmallCaps);
- continue
- }
- }
- if stretch.is_none() {
- if let Ok(value) = input.try_parse(FontStretchKeyword::parse) {
- stretch = Some(FontStretch::Keyword(value));
- continue
- }
- }
- size = Some(FontSize::parse(context, input)?);
- break
- }
-
- let size = match size {
- Some(s) => s,
- None => {
- return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError))
- }
- };
-
- let line_height = if input.try_parse(|input| input.expect_delim('/')).is_ok() {
- Some(LineHeight::parse(context, input)?)
- } else {
- None
- };
-
- #[inline]
- fn count<T>(opt: &Option<T>) -> u8 {
- if opt.is_some() { 1 } else { 0 }
- }
-
- if (count(&style) + count(&weight) + count(&variant_caps) + count(&stretch) + nb_normals) > 4 {
- return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError))
- }
-
- let family = FontFamily::parse(context, input)?;
- Ok(expanded! {
- % for name in "style weight stretch variant_caps".split():
- font_${name}: unwrap_or_initial!(font_${name}, ${name}),
- % endfor
- font_size: size,
- line_height: line_height.unwrap_or(LineHeight::normal()),
- font_family: family,
- % if engine == "gecko":
- % for name in gecko_sub_properties:
- font_${name}: font_${name}::get_initial_specified_value(),
- % endfor
- % endif
- })
- }
-
- % if engine == "gecko":
- enum CheckSystemResult {
- AllSystem(SystemFont),
- SomeSystem,
- None
- }
- % endif
-
- impl<'a> ToCss for LonghandsToSerialize<'a> {
- fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result where W: fmt::Write {
- % if engine == "gecko":
- match self.check_system() {
- CheckSystemResult::AllSystem(sys) => return sys.to_css(dest),
- CheckSystemResult::SomeSystem => return Ok(()),
- CheckSystemResult::None => {}
- }
- % endif
-
- % if engine == "gecko":
- if let Some(v) = self.font_optical_sizing {
- if v != &font_optical_sizing::get_initial_specified_value() {
- return Ok(());
- }
- }
- if let Some(v) = self.font_variation_settings {
- if v != &font_variation_settings::get_initial_specified_value() {
- return Ok(());
- }
- }
- if let Some(v) = self.font_variant_emoji {
- if v != &font_variant_emoji::get_initial_specified_value() {
- return Ok(());
- }
- }
-
- % for name in gecko_sub_properties:
- % if name != "optical_sizing" and name != "variation_settings" and name != "variant_emoji":
- if self.font_${name} != &font_${name}::get_initial_specified_value() {
- return Ok(());
- }
- % endif
- % endfor
- % endif
-
- // Only font-stretch keywords are allowed as part as the font
- // shorthand.
- let font_stretch = match *self.font_stretch {
- FontStretch::Keyword(kw) => kw,
- FontStretch::Stretch(percentage) => {
- match FontStretchKeyword::from_percentage(percentage.0.get()) {
- Some(kw) => kw,
- None => return Ok(()),
- }
- }
- FontStretch::System(..) => return Ok(()),
- };
-
- // The only variant-caps value allowed in the shorthand is small-caps (from CSS2);
- // the added values defined by CSS Fonts 3 and later are not supported.
- // https://www.w3.org/TR/css-fonts-4/#font-prop
- if self.font_variant_caps != &font_variant_caps::get_initial_specified_value() &&
- *self.font_variant_caps != SmallCaps {
- return Ok(());
- }
-
- % for name in "style variant_caps".split():
- if self.font_${name} != &font_${name}::get_initial_specified_value() {
- self.font_${name}.to_css(dest)?;
- dest.write_char(' ')?;
- }
- % endfor
-
- // The initial specified font-weight value of 'normal' computes as a number (400),
- // not to the keyword, so we need to check for that as well in order to properly
- // serialize the computed style.
- if self.font_weight != &FontWeight::normal() &&
- self.font_weight != &FontWeight::from_gecko_keyword(400) {
- self.font_weight.to_css(dest)?;
- dest.write_char(' ')?;
- }
-
- if font_stretch != FontStretchKeyword::Normal {
- font_stretch.to_css(dest)?;
- dest.write_char(' ')?;
- }
-
- self.font_size.to_css(dest)?;
-
- if *self.line_height != LineHeight::normal() {
- dest.write_str(" / ")?;
- self.line_height.to_css(dest)?;
- }
-
- dest.write_char(' ')?;
- self.font_family.to_css(dest)?;
-
- Ok(())
- }
- }
-
- impl<'a> LonghandsToSerialize<'a> {
- % if engine == "gecko":
- /// Check if some or all members are system fonts
- fn check_system(&self) -> CheckSystemResult {
- let mut sys = None;
- let mut all = true;
-
- % for prop in SYSTEM_FONT_LONGHANDS:
- % if prop == "font_optical_sizing" or prop == "font_variation_settings":
- if let Some(value) = self.${prop} {
- % else:
- {
- let value = self.${prop};
- % endif
- match value.get_system() {
- Some(s) => {
- debug_assert!(sys.is_none() || s == sys.unwrap());
- sys = Some(s);
- }
- None => {
- all = false;
- }
- }
- }
- % endfor
- if self.line_height != &LineHeight::normal() {
- all = false
- }
- if all {
- CheckSystemResult::AllSystem(sys.unwrap())
- } else if sys.is_some() {
- CheckSystemResult::SomeSystem
- } else {
- CheckSystemResult::None
- }
- }
- % endif
- }
-
- <%
- subprops_for_value_info = ["font_style", "font_weight", "font_stretch",
- "font_variant_caps", "font_size", "font_family"]
- subprops_for_value_info = [
- "<longhands::{}::SpecifiedValue as SpecifiedValueInfo>".format(p)
- for p in subprops_for_value_info
- ]
- %>
- impl SpecifiedValueInfo for Longhands {
- const SUPPORTED_TYPES: u8 = 0
- % for p in subprops_for_value_info:
- | ${p}::SUPPORTED_TYPES
- % endfor
- ;
-
- fn collect_completion_keywords(f: KeywordsCollectFn) {
- % for p in subprops_for_value_info:
- ${p}::collect_completion_keywords(f);
- % endfor
- % if engine == "gecko":
- <SystemFont as SpecifiedValueInfo>::collect_completion_keywords(f);
- % endif
- }
- }
-</%helpers:shorthand>
-
-<%helpers:shorthand name="font-variant"
- engines="gecko servo"
- servo_pref="layout.legacy_layout",
- flags="SHORTHAND_IN_GETCS"
- sub_properties="font-variant-caps
- ${'font-variant-alternates' if engine == 'gecko' else ''}
- ${'font-variant-east-asian' if engine == 'gecko' else ''}
- ${'font-variant-emoji' if engine == 'gecko' else ''}
- ${'font-variant-ligatures' if engine == 'gecko' else ''}
- ${'font-variant-numeric' if engine == 'gecko' else ''}
- ${'font-variant-position' if engine == 'gecko' else ''}"
- spec="https://drafts.csswg.org/css-fonts-3/#propdef-font-variant">
-% if engine == 'gecko':
- <% sub_properties = "ligatures caps alternates numeric east_asian position emoji".split() %>
-% else:
- <% sub_properties = ["caps"] %>
-% endif
-
-% for prop in sub_properties:
- use crate::properties::longhands::font_variant_${prop};
-% endfor
- #[allow(unused_imports)]
- use crate::values::specified::FontVariantLigatures;
-
- pub fn parse_value<'i, 't>(
- context: &ParserContext,
- input: &mut Parser<'i, 't>,
- ) -> Result<Longhands, ParseError<'i>> {
- % for prop in sub_properties:
- let mut ${prop} = None;
- % endfor
-
- if input.try_parse(|input| input.expect_ident_matching("normal")).is_ok() {
- // Leave the values to None, 'normal' is the initial value for all the sub properties.
- } else if input.try_parse(|input| input.expect_ident_matching("none")).is_ok() {
- // The 'none' value sets 'font-variant-ligatures' to 'none' and resets all other sub properties
- // to their initial value.
- % if engine == "gecko":
- ligatures = Some(FontVariantLigatures::NONE);
- % endif
- } else {
- let mut has_custom_value: bool = false;
- loop {
- if input.try_parse(|input| input.expect_ident_matching("normal")).is_ok() ||
- input.try_parse(|input| input.expect_ident_matching("none")).is_ok() {
- return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError))
- }
- % for prop in sub_properties:
- if ${prop}.is_none() {
- if let Ok(value) = input.try_parse(|i| font_variant_${prop}::parse(context, i)) {
- has_custom_value = true;
- ${prop} = Some(value);
- continue
- }
- }
- % endfor
-
- break
- }
-
- if !has_custom_value {
- return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError))
- }
- }
-
- Ok(expanded! {
- % for prop in sub_properties:
- font_variant_${prop}: unwrap_or_initial!(font_variant_${prop}, ${prop}),
- % endfor
- })
- }
-
- impl<'a> ToCss for LonghandsToSerialize<'a> {
- #[allow(unused_assignments)]
- fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result where W: fmt::Write {
-
- let has_none_ligatures =
- % if engine == "gecko":
- self.font_variant_ligatures == &FontVariantLigatures::NONE;
- % else:
- false;
- % endif
-
- const TOTAL_SUBPROPS: usize = ${len(sub_properties)};
- let mut nb_normals = 0;
- % for prop in sub_properties:
- % if prop == "emoji":
- if let Some(value) = self.font_variant_${prop} {
- % else:
- {
- let value = self.font_variant_${prop};
- % endif
- if value == &font_variant_${prop}::get_initial_specified_value() {
- nb_normals += 1;
- }
- }
- % if prop == "emoji":
- else {
- // The property was disabled, so we count it as 'normal' for the purpose
- // of deciding how the shorthand can be serialized.
- nb_normals += 1;
- }
- % endif
- % endfor
-
-
- if nb_normals > 0 && nb_normals == TOTAL_SUBPROPS {
- dest.write_str("normal")?;
- } else if has_none_ligatures {
- if nb_normals == TOTAL_SUBPROPS - 1 {
- // Serialize to 'none' if 'font-variant-ligatures' is set to 'none' and all other
- // font feature properties are reset to their initial value.
- dest.write_str("none")?;
- } else {
- return Ok(())
- }
- } else {
- let mut has_any = false;
- % for prop in sub_properties:
- % if prop == "emoji":
- if let Some(value) = self.font_variant_${prop} {
- % else:
- {
- let value = self.font_variant_${prop};
- % endif
- if value != &font_variant_${prop}::get_initial_specified_value() {
- if has_any {
- dest.write_char(' ')?;
- }
- has_any = true;
- value.to_css(dest)?;
- }
- }
- % endfor
- }
-
- Ok(())
- }
- }
-</%helpers:shorthand>
-
-<%helpers:shorthand name="font-synthesis"
- engines="gecko"
- flags="SHORTHAND_IN_GETCS"
- sub_properties="font-synthesis-weight font-synthesis-style font-synthesis-small-caps"
- derive_value_info="False"
- spec="https://drafts.csswg.org/css-fonts-3/#propdef-font-variant">
- <% sub_properties = ["weight", "style", "small_caps"] %>
-
- use crate::values::specified::FontSynthesis;
-
- pub fn parse_value<'i, 't>(
- _context: &ParserContext,
- input: &mut Parser<'i, 't>,
- ) -> Result<Longhands, ParseError<'i>> {
- % for prop in sub_properties:
- let mut ${prop} = FontSynthesis::None;
- % endfor
-
- if input.try_parse(|input| input.expect_ident_matching("none")).is_ok() {
- // Leave all the individual values as None
- } else {
- let mut has_custom_value = false;
- while !input.is_exhausted() {
- try_match_ident_ignore_ascii_case! { input,
- % for prop in sub_properties:
- "${prop.replace('_', '-')}" if ${prop} == FontSynthesis::None => {
- has_custom_value = true;
- ${prop} = FontSynthesis::Auto;
- continue;
- },
- % endfor
- }
- }
- if !has_custom_value {
- return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError));
- }
- }
-
- Ok(expanded! {
- % for prop in sub_properties:
- font_synthesis_${prop}: ${prop},
- % endfor
- })
- }
-
- impl<'a> ToCss for LonghandsToSerialize<'a> {
- fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result where W: fmt::Write {
- let mut has_any = false;
-
- % for prop in sub_properties:
- if self.font_synthesis_${prop} == &FontSynthesis::Auto {
- if has_any {
- dest.write_char(' ')?;
- }
- has_any = true;
- dest.write_str("${prop.replace('_', '-')}")?;
- }
- % endfor
-
- if !has_any {
- dest.write_str("none")?;
- }
-
- Ok(())
- }
- }
-
- // The shorthand takes the sub-property names of the longhands, and not the
- // 'auto' keyword like they do, so we can't automatically derive this.
- impl SpecifiedValueInfo for Longhands {
- fn collect_completion_keywords(f: KeywordsCollectFn) {
- f(&[
- "none",
- % for prop in sub_properties:
- "${prop.replace('_', '-')}",
- % endfor
- ]);
- }
- }
-</%helpers:shorthand>
diff --git a/components/style/properties/shorthands/inherited_svg.mako.rs b/components/style/properties/shorthands/inherited_svg.mako.rs
deleted file mode 100644
index 899fc6a4643..00000000000
--- a/components/style/properties/shorthands/inherited_svg.mako.rs
+++ /dev/null
@@ -1,38 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
-
-<%namespace name="helpers" file="/helpers.mako.rs" />
-
-<%helpers:shorthand
- name="marker"
- engines="gecko"
- sub_properties="marker-start marker-end marker-mid"
- spec="https://www.w3.org/TR/SVG2/painting.html#MarkerShorthand"
->
- use crate::values::specified::url::UrlOrNone;
-
- pub fn parse_value<'i, 't>(
- context: &ParserContext,
- input: &mut Parser<'i, 't>,
- ) -> Result<Longhands, ParseError<'i>> {
- use crate::parser::Parse;
- let url = UrlOrNone::parse(context, input)?;
-
- Ok(expanded! {
- marker_start: url.clone(),
- marker_mid: url.clone(),
- marker_end: url,
- })
- }
-
- impl<'a> ToCss for LonghandsToSerialize<'a> {
- fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result where W: fmt::Write {
- if self.marker_start == self.marker_mid && self.marker_mid == self.marker_end {
- self.marker_start.to_css(dest)
- } else {
- Ok(())
- }
- }
- }
-</%helpers:shorthand>
diff --git a/components/style/properties/shorthands/inherited_text.mako.rs b/components/style/properties/shorthands/inherited_text.mako.rs
deleted file mode 100644
index 9eb278da05c..00000000000
--- a/components/style/properties/shorthands/inherited_text.mako.rs
+++ /dev/null
@@ -1,91 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
-
-<%namespace name="helpers" file="/helpers.mako.rs" />
-
-<%helpers:shorthand
- name="text-emphasis"
- engines="gecko"
- sub_properties="text-emphasis-style text-emphasis-color"
- derive_serialize="True"
- spec="https://drafts.csswg.org/css-text-decor-3/#text-emphasis-property"
->
- use crate::properties::longhands::{text_emphasis_color, text_emphasis_style};
-
- pub fn parse_value<'i, 't>(
- context: &ParserContext,
- input: &mut Parser<'i, 't>,
- ) -> Result<Longhands, ParseError<'i>> {
- let mut color = None;
- let mut style = None;
-
- loop {
- if color.is_none() {
- if let Ok(value) = input.try_parse(|input| text_emphasis_color::parse(context, input)) {
- color = Some(value);
- continue
- }
- }
- if style.is_none() {
- if let Ok(value) = input.try_parse(|input| text_emphasis_style::parse(context, input)) {
- style = Some(value);
- continue
- }
- }
- break
- }
- if color.is_some() || style.is_some() {
- Ok(expanded! {
- text_emphasis_color: unwrap_or_initial!(text_emphasis_color, color),
- text_emphasis_style: unwrap_or_initial!(text_emphasis_style, style),
- })
- } else {
- Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError))
- }
- }
-</%helpers:shorthand>
-
-// CSS Compatibility
-// https://compat.spec.whatwg.org/
-<%helpers:shorthand name="-webkit-text-stroke"
- engines="gecko"
- sub_properties="-webkit-text-stroke-width
- -webkit-text-stroke-color"
- derive_serialize="True"
- spec="https://compat.spec.whatwg.org/#the-webkit-text-stroke">
- use crate::properties::longhands::{_webkit_text_stroke_color, _webkit_text_stroke_width};
-
- pub fn parse_value<'i, 't>(
- context: &ParserContext,
- input: &mut Parser<'i, 't>,
- ) -> Result<Longhands, ParseError<'i>> {
- let mut color = None;
- let mut width = None;
- loop {
- if color.is_none() {
- if let Ok(value) = input.try_parse(|input| _webkit_text_stroke_color::parse(context, input)) {
- color = Some(value);
- continue
- }
- }
-
- if width.is_none() {
- if let Ok(value) = input.try_parse(|input| _webkit_text_stroke_width::parse(context, input)) {
- width = Some(value);
- continue
- }
- }
- break
- }
-
- if color.is_some() || width.is_some() {
- Ok(expanded! {
- _webkit_text_stroke_color: unwrap_or_initial!(_webkit_text_stroke_color, color),
- _webkit_text_stroke_width: unwrap_or_initial!(_webkit_text_stroke_width, width),
- })
- } else {
- Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError))
- }
- }
-</%helpers:shorthand>
diff --git a/components/style/properties/shorthands/list.mako.rs b/components/style/properties/shorthands/list.mako.rs
deleted file mode 100644
index 183c5ab5daa..00000000000
--- a/components/style/properties/shorthands/list.mako.rs
+++ /dev/null
@@ -1,152 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
-
-<%namespace name="helpers" file="/helpers.mako.rs" />
-
-<%helpers:shorthand name="list-style"
- engines="gecko servo"
- sub_properties="list-style-position list-style-image list-style-type"
- spec="https://drafts.csswg.org/css-lists/#propdef-list-style">
- use crate::properties::longhands::{list_style_image, list_style_position, list_style_type};
- use crate::values::specified::Image;
-
- pub fn parse_value<'i, 't>(
- context: &ParserContext,
- input: &mut Parser<'i, 't>,
- ) -> Result<Longhands, ParseError<'i>> {
- // `none` is ambiguous until we've finished parsing the shorthands, so we count the number
- // of times we see it.
- let mut nones = 0u8;
- let (mut image, mut position, mut list_style_type, mut any) = (None, None, None, false);
- loop {
- if input.try_parse(|input| input.expect_ident_matching("none")).is_ok() {
- nones = nones + 1;
- if nones > 2 {
- return Err(input.new_custom_error(SelectorParseErrorKind::UnexpectedIdent("none".into())))
- }
- any = true;
- continue
- }
-
- if image.is_none() {
- if let Ok(value) = input.try_parse(|input| list_style_image::parse(context, input)) {
- image = Some(value);
- any = true;
- continue
- }
- }
-
- if position.is_none() {
- if let Ok(value) = input.try_parse(|input| list_style_position::parse(context, input)) {
- position = Some(value);
- any = true;
- continue
- }
- }
-
- // list-style-type must be checked the last, because it accepts
- // arbitrary identifier for custom counter style, and thus may
- // affect values of list-style-position.
- if list_style_type.is_none() {
- if let Ok(value) = input.try_parse(|input| list_style_type::parse(context, input)) {
- list_style_type = Some(value);
- any = true;
- continue
- }
- }
- break
- }
-
- let position = unwrap_or_initial!(list_style_position, position);
-
- // If there are two `none`s, then we can't have a type or image; if there is one `none`,
- // then we can't have both a type *and* an image; if there is no `none` then we're fine as
- // long as we parsed something.
- use self::list_style_type::SpecifiedValue as ListStyleType;
- match (any, nones, list_style_type, image) {
- (true, 2, None, None) => {
- Ok(expanded! {
- list_style_position: position,
- list_style_image: Image::None,
- list_style_type: ListStyleType::None,
- })
- }
- (true, 1, None, Some(image)) => {
- Ok(expanded! {
- list_style_position: position,
- list_style_image: image,
- list_style_type: ListStyleType::None,
- })
- }
- (true, 1, Some(list_style_type), None) => {
- Ok(expanded! {
- list_style_position: position,
- list_style_image: Image::None,
- list_style_type: list_style_type,
- })
- }
- (true, 1, None, None) => {
- Ok(expanded! {
- list_style_position: position,
- list_style_image: Image::None,
- list_style_type: ListStyleType::None,
- })
- }
- (true, 0, list_style_type, image) => {
- Ok(expanded! {
- list_style_position: position,
- list_style_image: unwrap_or_initial!(list_style_image, image),
- list_style_type: unwrap_or_initial!(list_style_type),
- })
- }
- _ => Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError)),
- }
- }
-
- impl<'a> ToCss for LonghandsToSerialize<'a> {
- fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result where W: fmt::Write {
- use longhands::list_style_position::SpecifiedValue as ListStylePosition;
- use longhands::list_style_type::SpecifiedValue as ListStyleType;
- use longhands::list_style_image::SpecifiedValue as ListStyleImage;
- let mut have_one_non_initial_value = false;
- #[cfg(feature = "gecko")]
- let position_is_initial = self.list_style_position == &ListStylePosition::Outside;
- #[cfg(feature = "servo")]
- let position_is_initial = matches!(self.list_style_position, None | Some(&ListStylePosition::Outside));
- if !position_is_initial {
- self.list_style_position.to_css(dest)?;
- have_one_non_initial_value = true;
- }
- if self.list_style_image != &ListStyleImage::None {
- if have_one_non_initial_value {
- dest.write_char(' ')?;
- }
- self.list_style_image.to_css(dest)?;
- have_one_non_initial_value = true;
- }
- #[cfg(feature = "gecko")]
- let type_is_initial = self.list_style_type == &ListStyleType::disc();
- #[cfg(feature = "servo")]
- let type_is_initial = self.list_style_type == &ListStyleType::Disc;
- if !type_is_initial {
- if have_one_non_initial_value {
- dest.write_char(' ')?;
- }
- self.list_style_type.to_css(dest)?;
- have_one_non_initial_value = true;
- }
- if !have_one_non_initial_value {
- #[cfg(feature = "gecko")]
- self.list_style_position.to_css(dest)?;
- #[cfg(feature = "servo")]
- if let Some(position) = self.list_style_position {
- position.to_css(dest)?;
- } else {
- self.list_style_type.to_css(dest)?;
- }
- }
- Ok(())
- }
- }
-</%helpers:shorthand>
diff --git a/components/style/properties/shorthands/margin.mako.rs b/components/style/properties/shorthands/margin.mako.rs
deleted file mode 100644
index ba994316a21..00000000000
--- a/components/style/properties/shorthands/margin.mako.rs
+++ /dev/null
@@ -1,60 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
-
-<%namespace name="helpers" file="/helpers.mako.rs" />
-<% from data import DEFAULT_RULES_AND_PAGE %>
-
-${helpers.four_sides_shorthand(
- "margin",
- "margin-%s",
- "specified::LengthPercentageOrAuto::parse",
- engines="gecko servo",
- spec="https://drafts.csswg.org/css-box/#propdef-margin",
- rule_types_allowed=DEFAULT_RULES_AND_PAGE,
- allow_quirks="Yes",
-)}
-
-${helpers.two_properties_shorthand(
- "margin-block",
- "margin-block-start",
- "margin-block-end",
- "specified::LengthPercentageOrAuto::parse",
- engines="gecko servo",
- spec="https://drafts.csswg.org/css-logical/#propdef-margin-block"
-)}
-
-${helpers.two_properties_shorthand(
- "margin-inline",
- "margin-inline-start",
- "margin-inline-end",
- "specified::LengthPercentageOrAuto::parse",
- engines="gecko servo",
- spec="https://drafts.csswg.org/css-logical/#propdef-margin-inline"
-)}
-
-${helpers.four_sides_shorthand(
- "scroll-margin",
- "scroll-margin-%s",
- "specified::Length::parse",
- engines="gecko",
- spec="https://drafts.csswg.org/css-scroll-snap-1/#propdef-scroll-margin",
-)}
-
-${helpers.two_properties_shorthand(
- "scroll-margin-block",
- "scroll-margin-block-start",
- "scroll-margin-block-end",
- "specified::Length::parse",
- engines="gecko",
- spec="https://drafts.csswg.org/css-scroll-snap-1/#propdef-scroll-margin-block",
-)}
-
-${helpers.two_properties_shorthand(
- "scroll-margin-inline",
- "scroll-margin-inline-start",
- "scroll-margin-inline-end",
- "specified::Length::parse",
- engines="gecko",
- spec="https://drafts.csswg.org/css-scroll-snap-1/#propdef-scroll-margin-inline",
-)}
diff --git a/components/style/properties/shorthands/outline.mako.rs b/components/style/properties/shorthands/outline.mako.rs
deleted file mode 100644
index ff77e1175e2..00000000000
--- a/components/style/properties/shorthands/outline.mako.rs
+++ /dev/null
@@ -1,80 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
-
-<%namespace name="helpers" file="/helpers.mako.rs" />
-
-<%helpers:shorthand name="outline"
- engines="gecko servo"
- sub_properties="outline-color outline-style outline-width"
- spec="https://drafts.csswg.org/css-ui/#propdef-outline">
- use crate::properties::longhands::{outline_color, outline_width, outline_style};
- use crate::values::specified;
- use crate::parser::Parse;
-
- pub fn parse_value<'i, 't>(
- context: &ParserContext,
- input: &mut Parser<'i, 't>,
- ) -> Result<Longhands, ParseError<'i>> {
- let _unused = context;
- let mut color = None;
- let mut style = None;
- let mut width = None;
- let mut any = false;
- loop {
- if color.is_none() {
- if let Ok(value) = input.try_parse(|i| specified::Color::parse(context, i)) {
- color = Some(value);
- any = true;
- continue
- }
- }
- if style.is_none() {
- if let Ok(value) = input.try_parse(|input| outline_style::parse(context, input)) {
- style = Some(value);
- any = true;
- continue
- }
- }
- if width.is_none() {
- if let Ok(value) = input.try_parse(|input| outline_width::parse(context, input)) {
- width = Some(value);
- any = true;
- continue
- }
- }
- break
- }
- if any {
- Ok(expanded! {
- outline_color: unwrap_or_initial!(outline_color, color),
- outline_style: unwrap_or_initial!(outline_style, style),
- outline_width: unwrap_or_initial!(outline_width, width),
- })
- } else {
- Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError))
- }
- }
-
- impl<'a> ToCss for LonghandsToSerialize<'a> {
- fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result where W: fmt::Write {
- let mut wrote_value = false;
-
- % for name in "color style width".split():
- if *self.outline_${name} != outline_${name}::get_initial_specified_value() {
- if wrote_value {
- dest.write_char(' ')?;
- }
- self.outline_${name}.to_css(dest)?;
- wrote_value = true;
- }
- % endfor
-
- if !wrote_value {
- self.outline_style.to_css(dest)?;
- }
-
- Ok(())
- }
- }
-</%helpers:shorthand>
diff --git a/components/style/properties/shorthands/padding.mako.rs b/components/style/properties/shorthands/padding.mako.rs
deleted file mode 100644
index dad0193708d..00000000000
--- a/components/style/properties/shorthands/padding.mako.rs
+++ /dev/null
@@ -1,58 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
-
-<%namespace name="helpers" file="/helpers.mako.rs" />
-
-${helpers.four_sides_shorthand(
- "padding",
- "padding-%s",
- "specified::NonNegativeLengthPercentage::parse",
- engines="gecko servo",
- spec="https://drafts.csswg.org/css-box-3/#propdef-padding",
- allow_quirks="Yes",
-)}
-
-${helpers.two_properties_shorthand(
- "padding-block",
- "padding-block-start",
- "padding-block-end",
- "specified::NonNegativeLengthPercentage::parse",
- engines="gecko servo",
- spec="https://drafts.csswg.org/css-logical/#propdef-padding-block"
-)}
-
-${helpers.two_properties_shorthand(
- "padding-inline",
- "padding-inline-start",
- "padding-inline-end",
- "specified::NonNegativeLengthPercentage::parse",
- engines="gecko servo",
- spec="https://drafts.csswg.org/css-logical/#propdef-padding-inline"
-)}
-
-${helpers.four_sides_shorthand(
- "scroll-padding",
- "scroll-padding-%s",
- "specified::NonNegativeLengthPercentageOrAuto::parse",
- engines="gecko",
- spec="https://drafts.csswg.org/css-scroll-snap-1/#propdef-scroll-padding"
-)}
-
-${helpers.two_properties_shorthand(
- "scroll-padding-block",
- "scroll-padding-block-start",
- "scroll-padding-block-end",
- "specified::NonNegativeLengthPercentageOrAuto::parse",
- engines="gecko",
- spec="https://drafts.csswg.org/css-scroll-snap-1/#propdef-scroll-padding-block"
-)}
-
-${helpers.two_properties_shorthand(
- "scroll-padding-inline",
- "scroll-padding-inline-start",
- "scroll-padding-inline-end",
- "specified::NonNegativeLengthPercentageOrAuto::parse",
- engines="gecko",
- spec="https://drafts.csswg.org/css-scroll-snap-1/#propdef-scroll-padding-inline"
-)}
diff --git a/components/style/properties/shorthands/position.mako.rs b/components/style/properties/shorthands/position.mako.rs
deleted file mode 100644
index 6d2b47d345f..00000000000
--- a/components/style/properties/shorthands/position.mako.rs
+++ /dev/null
@@ -1,893 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
-
-<%namespace name="helpers" file="/helpers.mako.rs" />
-
-<%helpers:shorthand name="flex-flow"
- engines="gecko servo",
- servo_pref="layout.flexbox.enabled",
- sub_properties="flex-direction flex-wrap"
- extra_prefixes="webkit"
- spec="https://drafts.csswg.org/css-flexbox/#flex-flow-property">
- use crate::properties::longhands::{flex_direction, flex_wrap};
-
- pub fn parse_value<'i, 't>(
- context: &ParserContext,
- input: &mut Parser<'i, 't>,
- ) -> Result<Longhands, ParseError<'i>> {
- let mut direction = None;
- let mut wrap = None;
- loop {
- if direction.is_none() {
- if let Ok(value) = input.try_parse(|input| flex_direction::parse(context, input)) {
- direction = Some(value);
- continue
- }
- }
- if wrap.is_none() {
- if let Ok(value) = input.try_parse(|input| flex_wrap::parse(context, input)) {
- wrap = Some(value);
- continue
- }
- }
- break
- }
-
- if direction.is_none() && wrap.is_none() {
- return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError))
- }
- Ok(expanded! {
- flex_direction: unwrap_or_initial!(flex_direction, direction),
- flex_wrap: unwrap_or_initial!(flex_wrap, wrap),
- })
- }
-
- impl<'a> ToCss for LonghandsToSerialize<'a> {
- fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result where W: fmt::Write {
- if *self.flex_direction == flex_direction::get_initial_specified_value() &&
- *self.flex_wrap != flex_wrap::get_initial_specified_value() {
- return self.flex_wrap.to_css(dest)
- }
- self.flex_direction.to_css(dest)?;
- if *self.flex_wrap != flex_wrap::get_initial_specified_value() {
- dest.write_char(' ')?;
- self.flex_wrap.to_css(dest)?;
- }
- Ok(())
- }
- }
-</%helpers:shorthand>
-
-<%helpers:shorthand name="flex"
- engines="gecko servo",
- servo_pref="layout.flexbox.enabled",
- sub_properties="flex-grow flex-shrink flex-basis"
- extra_prefixes="webkit"
- derive_serialize="True"
- spec="https://drafts.csswg.org/css-flexbox/#flex-property">
- use crate::parser::Parse;
- use crate::values::specified::NonNegativeNumber;
- use crate::properties::longhands::flex_basis::SpecifiedValue as FlexBasis;
-
- fn parse_flexibility<'i, 't>(
- context: &ParserContext,
- input: &mut Parser<'i, 't>,
- ) -> Result<(NonNegativeNumber, Option<NonNegativeNumber>),ParseError<'i>> {
- let grow = NonNegativeNumber::parse(context, input)?;
- let shrink = input.try_parse(|i| NonNegativeNumber::parse(context, i)).ok();
- Ok((grow, shrink))
- }
-
- pub fn parse_value<'i, 't>(
- context: &ParserContext,
- input: &mut Parser<'i, 't>,
- ) -> Result<Longhands, ParseError<'i>> {
- let mut grow = None;
- let mut shrink = None;
- let mut basis = None;
-
- if input.try_parse(|input| input.expect_ident_matching("none")).is_ok() {
- return Ok(expanded! {
- flex_grow: NonNegativeNumber::new(0.0),
- flex_shrink: NonNegativeNumber::new(0.0),
- flex_basis: FlexBasis::auto(),
- })
- }
- loop {
- if grow.is_none() {
- if let Ok((flex_grow, flex_shrink)) = input.try_parse(|i| parse_flexibility(context, i)) {
- grow = Some(flex_grow);
- shrink = flex_shrink;
- continue
- }
- }
- if basis.is_none() {
- if let Ok(value) = input.try_parse(|input| FlexBasis::parse(context, input)) {
- basis = Some(value);
- continue
- }
- }
- break
- }
-
- if grow.is_none() && basis.is_none() {
- return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError))
- }
- Ok(expanded! {
- flex_grow: grow.unwrap_or(NonNegativeNumber::new(1.0)),
- flex_shrink: shrink.unwrap_or(NonNegativeNumber::new(1.0)),
- // Per spec, this should be SpecifiedValue::zero(), but all
- // browsers currently agree on using `0%`. This is a spec
- // change which hasn't been adopted by browsers:
- // https://github.com/w3c/csswg-drafts/commit/2c446befdf0f686217905bdd7c92409f6bd3921b
- flex_basis: basis.unwrap_or(FlexBasis::zero_percent()),
- })
- }
-</%helpers:shorthand>
-
-<%helpers:shorthand
- name="gap"
- engines="gecko"
- aliases="grid-gap"
- sub_properties="row-gap column-gap"
- spec="https://drafts.csswg.org/css-align-3/#gap-shorthand"
->
- use crate::properties::longhands::{row_gap, column_gap};
-
- pub fn parse_value<'i, 't>(context: &ParserContext, input: &mut Parser<'i, 't>)
- -> Result<Longhands, ParseError<'i>> {
- let r_gap = row_gap::parse(context, input)?;
- let c_gap = input.try_parse(|input| column_gap::parse(context, input)).unwrap_or(r_gap.clone());
-
- Ok(expanded! {
- row_gap: r_gap,
- column_gap: c_gap,
- })
- }
-
- impl<'a> ToCss for LonghandsToSerialize<'a> {
- fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result where W: fmt::Write {
- if self.row_gap == self.column_gap {
- self.row_gap.to_css(dest)
- } else {
- self.row_gap.to_css(dest)?;
- dest.write_char(' ')?;
- self.column_gap.to_css(dest)
- }
- }
- }
-
-</%helpers:shorthand>
-
-% for kind in ["row", "column"]:
-<%helpers:shorthand
- name="grid-${kind}"
- sub_properties="grid-${kind}-start grid-${kind}-end"
- engines="gecko",
- spec="https://drafts.csswg.org/css-grid/#propdef-grid-${kind}"
->
- use crate::values::specified::GridLine;
- use crate::parser::Parse;
- use crate::Zero;
-
- // NOTE: Since both the shorthands have the same code, we should (re-)use code from one to implement
- // the other. This might not be a big deal for now, but we should consider looking into this in the future
- // to limit the amount of code generated.
- pub fn parse_value<'i, 't>(
- context: &ParserContext,
- input: &mut Parser<'i, 't>,
- ) -> Result<Longhands, ParseError<'i>> {
- let start = input.try_parse(|i| GridLine::parse(context, i))?;
- let end = if input.try_parse(|i| i.expect_delim('/')).is_ok() {
- GridLine::parse(context, input)?
- } else {
- let mut line = GridLine::auto();
- if start.line_num.is_zero() && !start.is_span {
- line.ident = start.ident.clone(); // ident from start value should be taken
- }
-
- line
- };
-
- Ok(expanded! {
- grid_${kind}_start: start,
- grid_${kind}_end: end,
- })
- }
-
- impl<'a> ToCss for LonghandsToSerialize<'a> {
- // Return the shortest possible serialization of the `grid-${kind}-[start/end]` values.
- // This function exploits the opportunities to omit the end value per this spec text:
- //
- // https://drafts.csswg.org/css-grid/#propdef-grid-column
- // "When the second value is omitted, if the first value is a <custom-ident>,
- // the grid-row-end/grid-column-end longhand is also set to that <custom-ident>;
- // otherwise, it is set to auto."
- fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result where W: fmt::Write {
- self.grid_${kind}_start.to_css(dest)?;
- if self.grid_${kind}_start.can_omit(self.grid_${kind}_end) {
- return Ok(()); // the end value is redundant
- }
- dest.write_str(" / ")?;
- self.grid_${kind}_end.to_css(dest)
- }
- }
-</%helpers:shorthand>
-% endfor
-
-<%helpers:shorthand
- name="grid-area"
- engines="gecko"
- sub_properties="grid-row-start grid-row-end grid-column-start grid-column-end"
- spec="https://drafts.csswg.org/css-grid/#propdef-grid-area"
->
- use crate::values::specified::GridLine;
- use crate::parser::Parse;
- use crate::Zero;
-
- // The code is the same as `grid-{row,column}` except that this can have four values at most.
- pub fn parse_value<'i, 't>(
- context: &ParserContext,
- input: &mut Parser<'i, 't>,
- ) -> Result<Longhands, ParseError<'i>> {
- fn line_with_ident_from(other: &GridLine) -> GridLine {
- let mut this = GridLine::auto();
- if other.line_num.is_zero() && !other.is_span {
- this.ident = other.ident.clone();
- }
-
- this
- }
-
- let row_start = input.try_parse(|i| GridLine::parse(context, i))?;
- let (column_start, row_end, column_end) = if input.try_parse(|i| i.expect_delim('/')).is_ok() {
- let column_start = GridLine::parse(context, input)?;
- let (row_end, column_end) = if input.try_parse(|i| i.expect_delim('/')).is_ok() {
- let row_end = GridLine::parse(context, input)?;
- let column_end = if input.try_parse(|i| i.expect_delim('/')).is_ok() {
- GridLine::parse(context, input)?
- } else { // grid-column-end has not been given
- line_with_ident_from(&column_start)
- };
-
- (row_end, column_end)
- } else { // grid-row-start and grid-column-start has been given
- let row_end = line_with_ident_from(&row_start);
- let column_end = line_with_ident_from(&column_start);
- (row_end, column_end)
- };
-
- (column_start, row_end, column_end)
- } else { // only grid-row-start is given
- let line = line_with_ident_from(&row_start);
- (line.clone(), line.clone(), line)
- };
-
- Ok(expanded! {
- grid_row_start: row_start,
- grid_row_end: row_end,
- grid_column_start: column_start,
- grid_column_end: column_end,
- })
- }
-
- impl<'a> ToCss for LonghandsToSerialize<'a> {
- // Return the shortest possible serialization of the `grid-[column/row]-[start/end]` values.
- // This function exploits the opportunities to omit trailing values per this spec text:
- //
- // https://drafts.csswg.org/css-grid/#propdef-grid-area
- // "If four <grid-line> values are specified, grid-row-start is set to the first value,
- // grid-column-start is set to the second value, grid-row-end is set to the third value,
- // and grid-column-end is set to the fourth value.
- //
- // When grid-column-end is omitted, if grid-column-start is a <custom-ident>,
- // grid-column-end is set to that <custom-ident>; otherwise, it is set to auto.
- //
- // When grid-row-end is omitted, if grid-row-start is a <custom-ident>, grid-row-end is
- // set to that <custom-ident>; otherwise, it is set to auto.
- //
- // When grid-column-start is omitted, if grid-row-start is a <custom-ident>, all four
- // longhands are set to that value. Otherwise, it is set to auto."
- fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result where W: fmt::Write {
- self.grid_row_start.to_css(dest)?;
- let mut trailing_values = 3;
- if self.grid_column_start.can_omit(self.grid_column_end) {
- trailing_values -= 1;
- if self.grid_row_start.can_omit(self.grid_row_end) {
- trailing_values -= 1;
- if self.grid_row_start.can_omit(self.grid_column_start) {
- trailing_values -= 1;
- }
- }
- }
- let values = [&self.grid_column_start, &self.grid_row_end, &self.grid_column_end];
- for value in values.iter().take(trailing_values) {
- dest.write_str(" / ")?;
- value.to_css(dest)?;
- }
- Ok(())
- }
- }
-</%helpers:shorthand>
-
-<%helpers:shorthand
- name="grid-template"
- engines="gecko"
- sub_properties="grid-template-rows grid-template-columns grid-template-areas"
- spec="https://drafts.csswg.org/css-grid/#propdef-grid-template"
->
- use crate::parser::Parse;
- use servo_arc::Arc;
- use crate::values::generics::grid::{TrackSize, TrackList};
- use crate::values::generics::grid::{TrackListValue, concat_serialize_idents};
- use crate::values::specified::{GridTemplateComponent, GenericGridTemplateComponent};
- use crate::values::specified::grid::parse_line_names;
- use crate::values::specified::position::{GridTemplateAreas, TemplateAreas, TemplateAreasArc};
-
- /// Parsing for `<grid-template>` shorthand (also used by `grid` shorthand).
- pub fn parse_grid_template<'i, 't>(
- context: &ParserContext,
- input: &mut Parser<'i, 't>,
- ) -> Result<(GridTemplateComponent, GridTemplateComponent, GridTemplateAreas), ParseError<'i>> {
- // Other shorthand sub properties also parse the `none` keyword and this shorthand
- // should know after this keyword there is nothing to parse. Otherwise it gets
- // confused and rejects the sub properties that contains `none`.
- <% keywords = {
- "none": "GenericGridTemplateComponent::None",
- }
- %>
- % for keyword, rust_type in keywords.items():
- if let Ok(x) = input.try_parse(|i| {
- if i.try_parse(|i| i.expect_ident_matching("${keyword}")).is_ok() {
- if !i.is_exhausted() {
- return Err(());
- }
- return Ok((${rust_type}, ${rust_type}, GridTemplateAreas::None));
- }
- Err(())
- }) {
- return Ok(x);
- }
- % endfor
-
- let first_line_names = input.try_parse(parse_line_names).unwrap_or_default();
- if let Ok(string) = input.try_parse(|i| i.expect_string().map(|s| s.as_ref().to_owned().into())) {
- let mut strings = vec![];
- let mut values = vec![];
- let mut line_names = vec![];
- line_names.push(first_line_names);
- strings.push(string);
- loop {
- let size = input.try_parse(|i| TrackSize::parse(context, i)).unwrap_or_default();
- values.push(TrackListValue::TrackSize(size));
- let mut names = input.try_parse(parse_line_names).unwrap_or_default();
- let more_names = input.try_parse(parse_line_names);
-
- match input.try_parse(|i| i.expect_string().map(|s| s.as_ref().to_owned().into())) {
- Ok(string) => {
- strings.push(string);
- if let Ok(v) = more_names {
- // We got `[names] [more_names] "string"` - merge the two name lists.
- let mut names_vec = names.into_vec();
- names_vec.extend(v.into_iter());
- names = names_vec.into();
- }
- line_names.push(names);
- },
- Err(e) => {
- if more_names.is_ok() {
- // We've parsed `"string" [names] [more_names]` but then failed to parse another `"string"`.
- // The grammar doesn't allow two trailing `<line-names>` so this is an invalid value.
- return Err(e.into());
- }
- // only the named area determines whether we should bail out
- line_names.push(names);
- break
- },
- };
- }
-
- if line_names.len() == values.len() {
- // should be one longer than track sizes
- line_names.push(Default::default());
- }
-
- let template_areas = TemplateAreas::from_vec(strings)
- .map_err(|()| input.new_custom_error(StyleParseErrorKind::UnspecifiedError))?;
- let template_rows = TrackList {
- values: values.into(),
- line_names: line_names.into(),
- auto_repeat_index: std::usize::MAX,
- };
-
- let template_cols = if input.try_parse(|i| i.expect_delim('/')).is_ok() {
- let value = GridTemplateComponent::parse_without_none(context, input)?;
- if let GenericGridTemplateComponent::TrackList(ref list) = value {
- if !list.is_explicit() {
- return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError))
- }
- }
-
- value
- } else {
- GridTemplateComponent::default()
- };
-
- Ok((
- GenericGridTemplateComponent::TrackList(Box::new(template_rows)),
- template_cols,
- GridTemplateAreas::Areas(TemplateAreasArc(Arc::new(template_areas)))
- ))
- } else {
- let mut template_rows = GridTemplateComponent::parse(context, input)?;
- if let GenericGridTemplateComponent::TrackList(ref mut list) = template_rows {
- // Fist line names are parsed already and it shouldn't be parsed again.
- // If line names are not empty, that means given property value is not acceptable
- if list.line_names[0].is_empty() {
- list.line_names[0] = first_line_names; // won't panic
- } else {
- return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError));
- }
- }
-
- input.expect_delim('/')?;
- Ok((template_rows, GridTemplateComponent::parse(context, input)?, GridTemplateAreas::None))
- }
- }
-
- #[inline]
- pub fn parse_value<'i, 't>(
- context: &ParserContext,
- input: &mut Parser<'i, 't>,
- ) -> Result<Longhands, ParseError<'i>> {
- let (rows, columns, areas) = parse_grid_template(context, input)?;
- Ok(expanded! {
- grid_template_rows: rows,
- grid_template_columns: columns,
- grid_template_areas: areas,
- })
- }
-
- /// Serialization for `<grid-template>` shorthand (also used by `grid` shorthand).
- pub fn serialize_grid_template<W>(
- template_rows: &GridTemplateComponent,
- template_columns: &GridTemplateComponent,
- template_areas: &GridTemplateAreas,
- dest: &mut CssWriter<W>,
- ) -> fmt::Result
- where
- W: Write {
- match *template_areas {
- GridTemplateAreas::None => {
- if template_rows.is_initial() && template_columns.is_initial() {
- return GridTemplateComponent::default().to_css(dest);
- }
- template_rows.to_css(dest)?;
- dest.write_str(" / ")?;
- template_columns.to_css(dest)
- },
- GridTemplateAreas::Areas(ref areas) => {
- // The length of template-area and template-rows values should be equal.
- if areas.0.strings.len() != template_rows.track_list_len() {
- return Ok(());
- }
-
- let track_list = match *template_rows {
- GenericGridTemplateComponent::TrackList(ref list) => {
- // We should fail if there is a `repeat` function.
- // `grid` and `grid-template` shorthands doesn't accept
- // that. Only longhand accepts.
- if !list.is_explicit() {
- return Ok(());
- }
- list
- },
- // Others template components shouldn't exist with normal shorthand values.
- // But if we need to serialize a group of longhand sub-properties for
- // the shorthand, we should be able to return empty string instead of crashing.
- _ => return Ok(()),
- };
-
- // We need to check some values that longhand accepts but shorthands don't.
- match *template_columns {
- // We should fail if there is a `repeat` function. `grid` and
- // `grid-template` shorthands doesn't accept that. Only longhand accepts that.
- GenericGridTemplateComponent::TrackList(ref list) => {
- if !list.is_explicit() {
- return Ok(());
- }
- },
- // Also the shorthands don't accept subgrids unlike longhand.
- // We should fail without an error here.
- GenericGridTemplateComponent::Subgrid(_) => {
- return Ok(());
- },
- _ => {},
- }
-
- let mut names_iter = track_list.line_names.iter();
- for (((i, string), names), value) in areas.0.strings.iter().enumerate()
- .zip(&mut names_iter)
- .zip(track_list.values.iter()) {
- if i > 0 {
- dest.write_char(' ')?;
- }
-
- if !names.is_empty() {
- concat_serialize_idents("[", "] ", names, " ", dest)?;
- }
-
- string.to_css(dest)?;
-
- // If the track size is the initial value then it's redundant here.
- if !value.is_initial() {
- dest.write_char(' ')?;
- value.to_css(dest)?;
- }
- }
-
- if let Some(names) = names_iter.next() {
- concat_serialize_idents(" [", "]", names, " ", dest)?;
- }
-
- if let GenericGridTemplateComponent::TrackList(ref list) = *template_columns {
- dest.write_str(" / ")?;
- list.to_css(dest)?;
- }
-
- Ok(())
- },
- }
- }
-
- impl<'a> ToCss for LonghandsToSerialize<'a> {
- #[inline]
- fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result where W: fmt::Write {
- serialize_grid_template(
- self.grid_template_rows,
- self.grid_template_columns,
- self.grid_template_areas,
- dest
- )
- }
- }
-</%helpers:shorthand>
-
-<%helpers:shorthand
- name="grid"
- engines="gecko"
- sub_properties="grid-template-rows grid-template-columns grid-template-areas
- grid-auto-rows grid-auto-columns grid-auto-flow"
- spec="https://drafts.csswg.org/css-grid/#propdef-grid"
->
- use crate::parser::Parse;
- use crate::properties::longhands::{grid_auto_columns, grid_auto_rows, grid_auto_flow};
- use crate::values::generics::grid::GridTemplateComponent;
- use crate::values::specified::{GenericGridTemplateComponent, ImplicitGridTracks};
- use crate::values::specified::position::{GridAutoFlow, GridTemplateAreas};
-
- pub fn parse_value<'i, 't>(
- context: &ParserContext,
- input: &mut Parser<'i, 't>,
- ) -> Result<Longhands, ParseError<'i>> {
- let mut temp_rows = GridTemplateComponent::default();
- let mut temp_cols = GridTemplateComponent::default();
- let mut temp_areas = GridTemplateAreas::None;
- let mut auto_rows = ImplicitGridTracks::default();
- let mut auto_cols = ImplicitGridTracks::default();
- let mut flow = grid_auto_flow::get_initial_value();
-
- fn parse_auto_flow<'i, 't>(
- input: &mut Parser<'i, 't>,
- is_row: bool,
- ) -> Result<GridAutoFlow, ParseError<'i>> {
- let mut track = None;
- let mut dense = GridAutoFlow::empty();
-
- for _ in 0..2 {
- if input.try_parse(|i| i.expect_ident_matching("auto-flow")).is_ok() {
- track = if is_row {
- Some(GridAutoFlow::ROW)
- } else {
- Some(GridAutoFlow::COLUMN)
- };
- } else if input.try_parse(|i| i.expect_ident_matching("dense")).is_ok() {
- dense = GridAutoFlow::DENSE
- } else {
- break
- }
- }
-
- if track.is_some() {
- Ok(track.unwrap() | dense)
- } else {
- Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError))
- }
- }
-
- if let Ok((rows, cols, areas)) = input.try_parse(|i| super::grid_template::parse_grid_template(context, i)) {
- temp_rows = rows;
- temp_cols = cols;
- temp_areas = areas;
- } else if let Ok(rows) = input.try_parse(|i| GridTemplateComponent::parse(context, i)) {
- temp_rows = rows;
- input.expect_delim('/')?;
- flow = parse_auto_flow(input, false)?;
- auto_cols = input.try_parse(|i| grid_auto_columns::parse(context, i)).unwrap_or_default();
- } else {
- flow = parse_auto_flow(input, true)?;
- auto_rows = input.try_parse(|i| grid_auto_rows::parse(context, i)).unwrap_or_default();
- input.expect_delim('/')?;
- temp_cols = GridTemplateComponent::parse(context, input)?;
- }
-
- Ok(expanded! {
- grid_template_rows: temp_rows,
- grid_template_columns: temp_cols,
- grid_template_areas: temp_areas,
- grid_auto_rows: auto_rows,
- grid_auto_columns: auto_cols,
- grid_auto_flow: flow,
- })
- }
-
- impl<'a> LonghandsToSerialize<'a> {
- /// Returns true if other sub properties except template-{rows,columns} are initial.
- fn is_grid_template(&self) -> bool {
- self.grid_auto_rows.is_initial() &&
- self.grid_auto_columns.is_initial() &&
- *self.grid_auto_flow == grid_auto_flow::get_initial_value()
- }
- }
-
- impl<'a> ToCss for LonghandsToSerialize<'a> {
- fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result where W: fmt::Write {
- if self.is_grid_template() {
- return super::grid_template::serialize_grid_template(
- self.grid_template_rows,
- self.grid_template_columns,
- self.grid_template_areas,
- dest
- );
- }
-
- if *self.grid_template_areas != GridTemplateAreas::None {
- // No other syntax can set the template areas, so fail to
- // serialize.
- return Ok(());
- }
-
- if self.grid_auto_flow.contains(GridAutoFlow::COLUMN) {
- // It should fail to serialize if other branch of the if condition's values are set.
- if !self.grid_auto_rows.is_initial() ||
- !self.grid_template_columns.is_initial() {
- return Ok(());
- }
-
- // It should fail to serialize if template-rows value is not Explicit.
- if let GenericGridTemplateComponent::TrackList(ref list) = *self.grid_template_rows {
- if !list.is_explicit() {
- return Ok(());
- }
- }
-
- self.grid_template_rows.to_css(dest)?;
- dest.write_str(" / auto-flow")?;
- if self.grid_auto_flow.contains(GridAutoFlow::DENSE) {
- dest.write_str(" dense")?;
- }
-
- if !self.grid_auto_columns.is_initial() {
- dest.write_char(' ')?;
- self.grid_auto_columns.to_css(dest)?;
- }
-
- return Ok(());
- }
-
- // It should fail to serialize if other branch of the if condition's values are set.
- if !self.grid_auto_columns.is_initial() ||
- !self.grid_template_rows.is_initial() {
- return Ok(());
- }
-
- // It should fail to serialize if template-column value is not Explicit.
- if let GenericGridTemplateComponent::TrackList(ref list) = *self.grid_template_columns {
- if !list.is_explicit() {
- return Ok(());
- }
- }
-
- dest.write_str("auto-flow")?;
- if self.grid_auto_flow.contains(GridAutoFlow::DENSE) {
- dest.write_str(" dense")?;
- }
-
- if !self.grid_auto_rows.is_initial() {
- dest.write_char(' ')?;
- self.grid_auto_rows.to_css(dest)?;
- }
-
- dest.write_str(" / ")?;
- self.grid_template_columns.to_css(dest)?;
- Ok(())
- }
- }
-</%helpers:shorthand>
-
-<%helpers:shorthand
- name="place-content"
- engines="gecko"
- sub_properties="align-content justify-content"
- spec="https://drafts.csswg.org/css-align/#propdef-place-content"
->
- use crate::values::specified::align::{AlignContent, JustifyContent, ContentDistribution, AxisDirection};
-
- pub fn parse_value<'i, 't>(
- _: &ParserContext,
- input: &mut Parser<'i, 't>,
- ) -> Result<Longhands, ParseError<'i>> {
- let align_content =
- ContentDistribution::parse(input, AxisDirection::Block)?;
-
- let justify_content = input.try_parse(|input| {
- ContentDistribution::parse(input, AxisDirection::Inline)
- });
-
- let justify_content = match justify_content {
- Ok(v) => v,
- Err(..) => {
- // https://drafts.csswg.org/css-align-3/#place-content:
- //
- // The second value is assigned to justify-content; if
- // omitted, it is copied from the first value, unless that
- // value is a <baseline-position> in which case it is
- // defaulted to start.
- //
- if !align_content.is_baseline_position() {
- align_content
- } else {
- ContentDistribution::start()
- }
- }
- };
-
- Ok(expanded! {
- align_content: AlignContent(align_content),
- justify_content: JustifyContent(justify_content),
- })
- }
-
- impl<'a> ToCss for LonghandsToSerialize<'a> {
- fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result where W: fmt::Write {
- self.align_content.to_css(dest)?;
- if self.align_content.0 != self.justify_content.0 {
- dest.write_char(' ')?;
- self.justify_content.to_css(dest)?;
- }
- Ok(())
- }
- }
-</%helpers:shorthand>
-
-<%helpers:shorthand
- name="place-self"
- engines="gecko"
- sub_properties="align-self justify-self"
- spec="https://drafts.csswg.org/css-align/#place-self-property"
->
- use crate::values::specified::align::{AlignSelf, JustifySelf, SelfAlignment, AxisDirection};
-
- pub fn parse_value<'i, 't>(
- _: &ParserContext,
- input: &mut Parser<'i, 't>,
- ) -> Result<Longhands, ParseError<'i>> {
- let align = SelfAlignment::parse(input, AxisDirection::Block)?;
- let justify = input.try_parse(|input| SelfAlignment::parse(input, AxisDirection::Inline));
-
- let justify = match justify {
- Ok(v) => v,
- Err(..) => {
- debug_assert!(align.is_valid_on_both_axes());
- align
- }
- };
-
- Ok(expanded! {
- align_self: AlignSelf(align),
- justify_self: JustifySelf(justify),
- })
- }
-
- impl<'a> ToCss for LonghandsToSerialize<'a> {
- fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result where W: fmt::Write {
- self.align_self.to_css(dest)?;
- if self.align_self.0 != self.justify_self.0 {
- dest.write_char(' ')?;
- self.justify_self.to_css(dest)?;
- }
- Ok(())
- }
- }
-</%helpers:shorthand>
-
-<%helpers:shorthand
- name="place-items"
- engines="gecko"
- sub_properties="align-items justify-items"
- spec="https://drafts.csswg.org/css-align/#place-items-property"
->
- use crate::values::specified::align::{AlignItems, JustifyItems};
- use crate::parser::Parse;
-
- impl From<AlignItems> for JustifyItems {
- fn from(align: AlignItems) -> JustifyItems {
- JustifyItems(align.0)
- }
- }
-
- pub fn parse_value<'i, 't>(
- context: &ParserContext,
- input: &mut Parser<'i, 't>,
- ) -> Result<Longhands, ParseError<'i>> {
- let align = AlignItems::parse(context, input)?;
- let justify =
- input.try_parse(|input| JustifyItems::parse(context, input))
- .unwrap_or_else(|_| JustifyItems::from(align));
-
- Ok(expanded! {
- align_items: align,
- justify_items: justify,
- })
- }
-
- impl<'a> ToCss for LonghandsToSerialize<'a> {
- fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result where W: fmt::Write {
- self.align_items.to_css(dest)?;
- if self.align_items.0 != self.justify_items.0 {
- dest.write_char(' ')?;
- self.justify_items.to_css(dest)?;
- }
-
- Ok(())
- }
- }
-</%helpers:shorthand>
-
-// See https://github.com/w3c/csswg-drafts/issues/3525 for the quirks stuff.
-${helpers.four_sides_shorthand(
- "inset",
- "%s",
- "specified::LengthPercentageOrAuto::parse",
- engines="gecko servo",
- spec="https://drafts.csswg.org/css-logical/#propdef-inset",
- allow_quirks="No",
-)}
-
-${helpers.two_properties_shorthand(
- "inset-block",
- "inset-block-start",
- "inset-block-end",
- "specified::LengthPercentageOrAuto::parse",
- engines="gecko servo",
- spec="https://drafts.csswg.org/css-logical/#propdef-inset-block"
-)}
-
-${helpers.two_properties_shorthand(
- "inset-inline",
- "inset-inline-start",
- "inset-inline-end",
- "specified::LengthPercentageOrAuto::parse",
- engines="gecko servo",
- spec="https://drafts.csswg.org/css-logical/#propdef-inset-inline"
-)}
-
-${helpers.two_properties_shorthand(
- "contain-intrinsic-size",
- "contain-intrinsic-width",
- "contain-intrinsic-height",
- engines="gecko",
- gecko_pref="layout.css.contain-intrinsic-size.enabled",
- spec="https://drafts.csswg.org/css-sizing-4/#intrinsic-size-override",
-)}
diff --git a/components/style/properties/shorthands/svg.mako.rs b/components/style/properties/shorthands/svg.mako.rs
deleted file mode 100644
index 97cce9ad04d..00000000000
--- a/components/style/properties/shorthands/svg.mako.rs
+++ /dev/null
@@ -1,258 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
-
-<%namespace name="helpers" file="/helpers.mako.rs" />
-
-<%helpers:shorthand name="mask" engines="gecko" extra_prefixes="webkit"
- flags="SHORTHAND_IN_GETCS"
- sub_properties="mask-mode mask-repeat mask-clip mask-origin mask-composite mask-position-x
- mask-position-y mask-size mask-image"
- spec="https://drafts.fxtf.org/css-masking/#propdef-mask">
- use crate::properties::longhands::{mask_mode, mask_repeat, mask_clip, mask_origin, mask_composite, mask_position_x,
- mask_position_y};
- use crate::properties::longhands::{mask_size, mask_image};
- use crate::values::specified::{Position, PositionComponent};
- use crate::parser::Parse;
-
- // FIXME(emilio): These two mask types should be the same!
- impl From<mask_origin::single_value::SpecifiedValue> for mask_clip::single_value::SpecifiedValue {
- fn from(origin: mask_origin::single_value::SpecifiedValue) -> mask_clip::single_value::SpecifiedValue {
- match origin {
- mask_origin::single_value::SpecifiedValue::ContentBox =>
- mask_clip::single_value::SpecifiedValue::ContentBox,
- mask_origin::single_value::SpecifiedValue::PaddingBox =>
- mask_clip::single_value::SpecifiedValue::PaddingBox ,
- mask_origin::single_value::SpecifiedValue::BorderBox =>
- mask_clip::single_value::SpecifiedValue::BorderBox,
- % if engine == "gecko":
- mask_origin::single_value::SpecifiedValue::FillBox =>
- mask_clip::single_value::SpecifiedValue::FillBox ,
- mask_origin::single_value::SpecifiedValue::StrokeBox =>
- mask_clip::single_value::SpecifiedValue::StrokeBox,
- mask_origin::single_value::SpecifiedValue::ViewBox=>
- mask_clip::single_value::SpecifiedValue::ViewBox,
- % endif
- }
- }
- }
-
- pub fn parse_value<'i, 't>(
- context: &ParserContext,
- input: &mut Parser<'i, 't>,
- ) -> Result<Longhands, ParseError<'i>> {
- % for name in "image mode position_x position_y size repeat origin clip composite".split():
- // Vec grows from 0 to 4 by default on first push(). So allocate with
- // capacity 1, so in the common case of only one item we don't way
- // overallocate, then shrink. Note that we always push at least one
- // item if parsing succeeds.
- let mut mask_${name} = Vec::with_capacity(1);
- % endfor
-
- input.parse_comma_separated(|input| {
- % for name in "image mode position size repeat origin clip composite".split():
- let mut ${name} = None;
- % endfor
- loop {
- if image.is_none() {
- if let Ok(value) = input.try_parse(|input| mask_image::single_value
- ::parse(context, input)) {
- image = Some(value);
- continue
- }
- }
- if position.is_none() {
- if let Ok(value) = input.try_parse(|input| Position::parse(context, input)) {
- position = Some(value);
-
- // Parse mask size, if applicable.
- size = input.try_parse(|input| {
- input.expect_delim('/')?;
- mask_size::single_value::parse(context, input)
- }).ok();
-
- continue
- }
- }
- % for name in "repeat origin clip composite mode".split():
- if ${name}.is_none() {
- if let Ok(value) = input.try_parse(|input| mask_${name}::single_value
- ::parse(context, input)) {
- ${name} = Some(value);
- continue
- }
- }
- % endfor
- break
- }
- if clip.is_none() {
- if let Some(origin) = origin {
- clip = Some(mask_clip::single_value::SpecifiedValue::from(origin));
- }
- }
- let mut any = false;
- % for name in "image mode position size repeat origin clip composite".split():
- any = any || ${name}.is_some();
- % endfor
- if any {
- if let Some(position) = position {
- mask_position_x.push(position.horizontal);
- mask_position_y.push(position.vertical);
- } else {
- mask_position_x.push(PositionComponent::zero());
- mask_position_y.push(PositionComponent::zero());
- }
- % for name in "image mode size repeat origin clip composite".split():
- if let Some(m_${name}) = ${name} {
- mask_${name}.push(m_${name});
- } else {
- mask_${name}.push(mask_${name}::single_value
- ::get_initial_specified_value());
- }
- % endfor
- Ok(())
- } else {
- Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError))
- }
- })?;
-
- Ok(expanded! {
- % for name in "image mode position_x position_y size repeat origin clip composite".split():
- mask_${name}: mask_${name}::SpecifiedValue(mask_${name}.into()),
- % endfor
- })
- }
-
- impl<'a> ToCss for LonghandsToSerialize<'a> {
- fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result where W: fmt::Write {
- use crate::properties::longhands::mask_origin::single_value::computed_value::T as Origin;
- use crate::properties::longhands::mask_clip::single_value::computed_value::T as Clip;
-
- let len = self.mask_image.0.len();
- if len == 0 {
- return Ok(());
- }
- % for name in "mode position_x position_y size repeat origin clip composite".split():
- if self.mask_${name}.0.len() != len {
- return Ok(());
- }
- % endfor
-
- for i in 0..len {
- if i > 0 {
- dest.write_str(", ")?;
- }
-
- % for name in "image mode position_x position_y size repeat origin clip composite".split():
- let ${name} = &self.mask_${name}.0[i];
- % endfor
-
- image.to_css(dest)?;
-
- if *mode != mask_mode::single_value::get_initial_specified_value() {
- dest.write_char(' ')?;
- mode.to_css(dest)?;
- }
-
- if *position_x != PositionComponent::zero() ||
- *position_y != PositionComponent::zero() ||
- *size != mask_size::single_value::get_initial_specified_value()
- {
- dest.write_char(' ')?;
- Position {
- horizontal: position_x.clone(),
- vertical: position_y.clone()
- }.to_css(dest)?;
-
- if *size != mask_size::single_value::get_initial_specified_value() {
- dest.write_str(" / ")?;
- size.to_css(dest)?;
- }
- }
-
- if *repeat != mask_repeat::single_value::get_initial_specified_value() {
- dest.write_char(' ')?;
- repeat.to_css(dest)?;
- }
-
- if *origin != Origin::BorderBox || *clip != Clip::BorderBox {
- dest.write_char(' ')?;
- origin.to_css(dest)?;
- if *clip != From::from(*origin) {
- dest.write_char(' ')?;
- clip.to_css(dest)?;
- }
- }
-
- if *composite != mask_composite::single_value::get_initial_specified_value() {
- dest.write_char(' ')?;
- composite.to_css(dest)?;
- }
- }
-
- Ok(())
- }
- }
-</%helpers:shorthand>
-
-<%helpers:shorthand name="mask-position" engines="gecko" extra_prefixes="webkit"
- flags="SHORTHAND_IN_GETCS"
- sub_properties="mask-position-x mask-position-y"
- spec="https://drafts.csswg.org/css-masks-4/#the-mask-position">
- use crate::properties::longhands::{mask_position_x,mask_position_y};
- use crate::values::specified::position::Position;
- use crate::parser::Parse;
-
- pub fn parse_value<'i, 't>(
- context: &ParserContext,
- input: &mut Parser<'i, 't>,
- ) -> Result<Longhands, ParseError<'i>> {
- // Vec grows from 0 to 4 by default on first push(). So allocate with
- // capacity 1, so in the common case of only one item we don't way
- // overallocate, then shrink. Note that we always push at least one
- // item if parsing succeeds.
- let mut position_x = Vec::with_capacity(1);
- let mut position_y = Vec::with_capacity(1);
- let mut any = false;
-
- input.parse_comma_separated(|input| {
- let value = Position::parse(context, input)?;
- position_x.push(value.horizontal);
- position_y.push(value.vertical);
- any = true;
- Ok(())
- })?;
-
- if !any {
- return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError));
- }
-
-
- Ok(expanded! {
- mask_position_x: mask_position_x::SpecifiedValue(position_x.into()),
- mask_position_y: mask_position_y::SpecifiedValue(position_y.into()),
- })
- }
-
- impl<'a> ToCss for LonghandsToSerialize<'a> {
- fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result where W: fmt::Write {
- let len = self.mask_position_x.0.len();
- if len == 0 || self.mask_position_y.0.len() != len {
- return Ok(());
- }
-
- for i in 0..len {
- Position {
- horizontal: self.mask_position_x.0[i].clone(),
- vertical: self.mask_position_y.0[i].clone()
- }.to_css(dest)?;
-
- if i < len - 1 {
- dest.write_str(", ")?;
- }
- }
-
- Ok(())
- }
- }
-</%helpers:shorthand>
diff --git a/components/style/properties/shorthands/text.mako.rs b/components/style/properties/shorthands/text.mako.rs
deleted file mode 100644
index 34d3cd5c5db..00000000000
--- a/components/style/properties/shorthands/text.mako.rs
+++ /dev/null
@@ -1,120 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
-
-<%namespace name="helpers" file="/helpers.mako.rs" />
-
-<%helpers:shorthand name="text-decoration"
- engines="gecko servo"
- flags="SHORTHAND_IN_GETCS"
- sub_properties="text-decoration-line
- ${' text-decoration-style text-decoration-color text-decoration-thickness' if engine == 'gecko' else ''}"
- spec="https://drafts.csswg.org/css-text-decor/#propdef-text-decoration">
- % if engine == "gecko":
- use crate::values::specified;
- use crate::properties::longhands::{text_decoration_style, text_decoration_color, text_decoration_thickness};
- % endif
- use crate::properties::longhands::text_decoration_line;
-
- pub fn parse_value<'i, 't>(
- context: &ParserContext,
- input: &mut Parser<'i, 't>,
- ) -> Result<Longhands, ParseError<'i>> {
- % if engine == "gecko":
- let (mut line, mut style, mut color, mut thickness, mut any) = (None, None, None, None, false);
- % else:
- let (mut line, mut any) = (None, false);
- % endif
-
- loop {
- macro_rules! parse_component {
- ($value:ident, $module:ident) => (
- if $value.is_none() {
- if let Ok(value) = input.try_parse(|input| $module::parse(context, input)) {
- $value = Some(value);
- any = true;
- continue;
- }
- }
- )
- }
-
- parse_component!(line, text_decoration_line);
-
- % if engine == "gecko":
- parse_component!(style, text_decoration_style);
- parse_component!(color, text_decoration_color);
- parse_component!(thickness, text_decoration_thickness);
- % endif
-
- break;
- }
-
- if !any {
- return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError));
- }
-
- Ok(expanded! {
- text_decoration_line: unwrap_or_initial!(text_decoration_line, line),
-
- % if engine == "gecko":
- text_decoration_style: unwrap_or_initial!(text_decoration_style, style),
- text_decoration_color: unwrap_or_initial!(text_decoration_color, color),
- text_decoration_thickness: unwrap_or_initial!(text_decoration_thickness, thickness),
- % endif
- })
- }
-
- impl<'a> ToCss for LonghandsToSerialize<'a> {
- #[allow(unused)]
- fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result where W: fmt::Write {
- use crate::values::specified::TextDecorationLine;
-
- let (is_solid_style, is_current_color, is_auto_thickness) =
- (
- % if engine == "gecko":
- *self.text_decoration_style == text_decoration_style::SpecifiedValue::Solid,
- *self.text_decoration_color == specified::Color::CurrentColor,
- self.text_decoration_thickness.is_auto()
- % else:
- true, true, true
- % endif
- );
-
- let mut has_value = false;
- let is_none = *self.text_decoration_line == TextDecorationLine::none();
- if (is_solid_style && is_current_color && is_auto_thickness) || !is_none {
- self.text_decoration_line.to_css(dest)?;
- has_value = true;
- }
-
- % if engine == "gecko":
- if !is_auto_thickness {
- if has_value {
- dest.write_char(' ')?;
- }
- self.text_decoration_thickness.to_css(dest)?;
- has_value = true;
- }
-
- if !is_solid_style {
- if has_value {
- dest.write_char(' ')?;
- }
- self.text_decoration_style.to_css(dest)?;
- has_value = true;
- }
-
- if !is_current_color {
- if has_value {
- dest.write_char(' ')?;
- }
- self.text_decoration_color.to_css(dest)?;
- has_value = true;
- }
- % endif
-
- Ok(())
- }
- }
-</%helpers:shorthand>
diff --git a/components/style/properties/shorthands/ui.mako.rs b/components/style/properties/shorthands/ui.mako.rs
deleted file mode 100644
index 8fefb89a837..00000000000
--- a/components/style/properties/shorthands/ui.mako.rs
+++ /dev/null
@@ -1,427 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
-
-<%namespace name="helpers" file="/helpers.mako.rs" />
-
-macro_rules! try_parse_one {
- ($context: expr, $input: expr, $var: ident, $prop_module: ident) => {
- if $var.is_none() {
- if let Ok(value) = $input.try_parse(|i| {
- $prop_module::single_value::parse($context, i)
- }) {
- $var = Some(value);
- continue;
- }
- }
- };
-}
-
-<%helpers:shorthand name="transition"
- engines="gecko servo"
- extra_prefixes="moz:layout.css.prefixes.transitions webkit"
- sub_properties="transition-property transition-duration
- transition-timing-function
- transition-delay"
- spec="https://drafts.csswg.org/css-transitions/#propdef-transition">
- use crate::parser::Parse;
- % for prop in "delay duration property timing_function".split():
- use crate::properties::longhands::transition_${prop};
- % endfor
- use crate::values::specified::TransitionProperty;
-
- pub fn parse_value<'i, 't>(
- context: &ParserContext,
- input: &mut Parser<'i, 't>,
- ) -> Result<Longhands, ParseError<'i>> {
- struct SingleTransition {
- % for prop in "duration timing_function delay".split():
- transition_${prop}: transition_${prop}::SingleSpecifiedValue,
- % endfor
- // Unlike other properties, transition-property uses an Option<> to
- // represent 'none' as `None`.
- transition_property: Option<TransitionProperty>,
- }
-
- fn parse_one_transition<'i, 't>(
- context: &ParserContext,
- input: &mut Parser<'i, 't>,
- ) -> Result<SingleTransition,ParseError<'i>> {
- % for prop in "property duration timing_function delay".split():
- let mut ${prop} = None;
- % endfor
-
- let mut parsed = 0;
- loop {
- parsed += 1;
-
- try_parse_one!(context, input, duration, transition_duration);
- try_parse_one!(context, input, timing_function, transition_timing_function);
- try_parse_one!(context, input, delay, transition_delay);
- // Must check 'transition-property' after 'transition-timing-function' since
- // 'transition-property' accepts any keyword.
- if property.is_none() {
- if let Ok(value) = input.try_parse(|i| TransitionProperty::parse(context, i)) {
- property = Some(Some(value));
- continue;
- }
-
- if input.try_parse(|i| i.expect_ident_matching("none")).is_ok() {
- // 'none' is not a valid value for <single-transition-property>,
- // so it's not acceptable in the function above.
- property = Some(None);
- continue;
- }
- }
-
- parsed -= 1;
- break
- }
-
- if parsed != 0 {
- Ok(SingleTransition {
- % for prop in "duration timing_function delay".split():
- transition_${prop}: ${prop}.unwrap_or_else(transition_${prop}::single_value
- ::get_initial_specified_value),
- % endfor
- transition_property: property.unwrap_or(
- Some(transition_property::single_value::get_initial_specified_value())),
- })
- } else {
- Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError))
- }
- }
-
- % for prop in "property duration timing_function delay".split():
- let mut ${prop}s = Vec::new();
- % endfor
-
- let results = input.parse_comma_separated(|i| parse_one_transition(context, i))?;
- let multiple_items = results.len() >= 2;
- for result in results {
- if let Some(value) = result.transition_property {
- propertys.push(value);
- } else if multiple_items {
- // If there is more than one item, and any of transitions has 'none',
- // then it's invalid. Othersize, leave propertys to be empty (which
- // means "transition-property: none");
- return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError));
- }
-
- % for prop in "duration timing_function delay".split():
- ${prop}s.push(result.transition_${prop});
- % endfor
- }
-
- Ok(expanded! {
- % for prop in "property duration timing_function delay".split():
- transition_${prop}: transition_${prop}::SpecifiedValue(${prop}s.into()),
- % endfor
- })
- }
-
- impl<'a> ToCss for LonghandsToSerialize<'a> {
- fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result where W: fmt::Write {
- let property_len = self.transition_property.0.len();
-
- // There are two cases that we can do shorthand serialization:
- // * when all value lists have the same length, or
- // * when transition-property is none, and other value lists have exactly one item.
- if property_len == 0 {
- % for name in "duration delay timing_function".split():
- if self.transition_${name}.0.len() != 1 {
- return Ok(());
- }
- % endfor
- } else {
- % for name in "duration delay timing_function".split():
- if self.transition_${name}.0.len() != property_len {
- return Ok(());
- }
- % endfor
- }
-
- // Representative length.
- let len = self.transition_duration.0.len();
-
- for i in 0..len {
- if i != 0 {
- dest.write_str(", ")?;
- }
- if property_len == 0 {
- dest.write_str("none")?;
- } else {
- self.transition_property.0[i].to_css(dest)?;
- }
- % for name in "duration timing_function delay".split():
- dest.write_char(' ')?;
- self.transition_${name}.0[i].to_css(dest)?;
- % endfor
- }
- Ok(())
- }
- }
-</%helpers:shorthand>
-
-<%helpers:shorthand name="animation"
- engines="gecko servo"
- extra_prefixes="moz:layout.css.prefixes.animations webkit"
- sub_properties="animation-name animation-duration
- animation-timing-function animation-delay
- animation-iteration-count animation-direction
- animation-fill-mode animation-play-state animation-timeline"
- rule_types_allowed="Style"
- spec="https://drafts.csswg.org/css-animations/#propdef-animation">
- <%
- props = "name timeline duration timing_function delay iteration_count \
- direction fill_mode play_state".split()
- %>
- % for prop in props:
- use crate::properties::longhands::animation_${prop};
- % endfor
-
- pub fn parse_value<'i, 't>(
- context: &ParserContext,
- input: &mut Parser<'i, 't>,
- ) -> Result<Longhands, ParseError<'i>> {
- struct SingleAnimation {
- % for prop in props:
- animation_${prop}: animation_${prop}::SingleSpecifiedValue,
- % endfor
- }
-
- fn parse_one_animation<'i, 't>(
- context: &ParserContext,
- input: &mut Parser<'i, 't>,
- ) -> Result<SingleAnimation, ParseError<'i>> {
- % for prop in props:
- let mut ${prop} = None;
- % endfor
-
- let mut parsed = 0;
- // NB: Name must be the last one here so that keywords valid for other
- // longhands are not interpreted as names.
- //
- // Also, duration must be before delay, see
- // https://drafts.csswg.org/css-animations/#typedef-single-animation
- loop {
- parsed += 1;
- try_parse_one!(context, input, duration, animation_duration);
- try_parse_one!(context, input, timing_function, animation_timing_function);
- try_parse_one!(context, input, delay, animation_delay);
- try_parse_one!(context, input, iteration_count, animation_iteration_count);
- try_parse_one!(context, input, direction, animation_direction);
- try_parse_one!(context, input, fill_mode, animation_fill_mode);
- try_parse_one!(context, input, play_state, animation_play_state);
- try_parse_one!(context, input, name, animation_name);
- if static_prefs::pref!("layout.css.scroll-driven-animations.enabled") {
- try_parse_one!(context, input, timeline, animation_timeline);
- }
-
- parsed -= 1;
- break
- }
-
- // If nothing is parsed, this is an invalid entry.
- if parsed == 0 {
- Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError))
- } else {
- Ok(SingleAnimation {
- % for prop in props:
- animation_${prop}: ${prop}.unwrap_or_else(animation_${prop}::single_value
- ::get_initial_specified_value),
- % endfor
- })
- }
- }
-
- % for prop in props:
- let mut ${prop}s = vec![];
- % endfor
-
- let results = input.parse_comma_separated(|i| parse_one_animation(context, i))?;
- for result in results.into_iter() {
- % for prop in props:
- ${prop}s.push(result.animation_${prop});
- % endfor
- }
-
- Ok(expanded! {
- % for prop in props:
- animation_${prop}: animation_${prop}::SpecifiedValue(${prop}s.into()),
- % endfor
- })
- }
-
- impl<'a> ToCss for LonghandsToSerialize<'a> {
- fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result where W: fmt::Write {
- let len = self.animation_name.0.len();
- // There should be at least one declared value
- if len == 0 {
- return Ok(());
- }
-
- // If any value list length is differs then we don't do a shorthand serialization
- // either.
- % for name in props[2:]:
- if len != self.animation_${name}.0.len() {
- return Ok(())
- }
- % endfor
-
- // If the preference of animation-timeline is disabled, `self.animation_timeline` is
- // None.
- if self.animation_timeline.map_or(false, |v| len != v.0.len()) {
- return Ok(());
- }
-
- for i in 0..len {
- if i != 0 {
- dest.write_str(", ")?;
- }
-
- % for name in props[2:]:
- self.animation_${name}.0[i].to_css(dest)?;
- dest.write_char(' ')?;
- % endfor
-
- self.animation_name.0[i].to_css(dest)?;
-
- // Based on the spec, the default values of other properties must be output in at
- // least the cases necessary to distinguish an animation-name. The serialization
- // order of animation-timeline is always later than animation-name, so it's fine
- // to not serialize it if it is the default value. It's still possible to
- // distinguish them (because we always serialize animation-name).
- // https://drafts.csswg.org/css-animations-1/#animation
- // https://drafts.csswg.org/css-animations-2/#typedef-single-animation
- //
- // Note: it's also fine to always serialize this. However, it seems Blink
- // doesn't serialize default animation-timeline now, so we follow the same rule.
- if let Some(ref timeline) = self.animation_timeline {
- if !timeline.0[i].is_auto() {
- dest.write_char(' ')?;
- timeline.0[i].to_css(dest)?;
- }
- }
- }
- Ok(())
- }
- }
-</%helpers:shorthand>
-
-<%helpers:shorthand
- engines="gecko"
- name="scroll-timeline"
- sub_properties="scroll-timeline-name scroll-timeline-axis"
- gecko_pref="layout.css.scroll-driven-animations.enabled",
- spec="https://drafts.csswg.org/scroll-animations-1/#scroll-timeline-shorthand"
->
- pub fn parse_value<'i>(
- context: &ParserContext,
- input: &mut Parser<'i, '_>,
- ) -> Result<Longhands, ParseError<'i>> {
- use crate::properties::longhands::{scroll_timeline_axis, scroll_timeline_name};
-
- let mut names = Vec::with_capacity(1);
- let mut axes = Vec::with_capacity(1);
- input.parse_comma_separated(|input| {
- let name = scroll_timeline_name::single_value::parse(context, input)?;
- let axis = input.try_parse(|i| scroll_timeline_axis::single_value::parse(context, i));
-
- names.push(name);
- axes.push(axis.unwrap_or_default());
-
- Ok(())
- })?;
-
- Ok(expanded! {
- scroll_timeline_name: scroll_timeline_name::SpecifiedValue(names.into()),
- scroll_timeline_axis: scroll_timeline_axis::SpecifiedValue(axes.into()),
- })
- }
-
- impl<'a> ToCss for LonghandsToSerialize<'a> {
- fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result where W: fmt::Write {
- // If any value list length is differs then we don't do a shorthand serialization
- // either.
- let len = self.scroll_timeline_name.0.len();
- if len != self.scroll_timeline_axis.0.len() {
- return Ok(());
- }
-
- for i in 0..len {
- if i != 0 {
- dest.write_str(", ")?;
- }
-
- self.scroll_timeline_name.0[i].to_css(dest)?;
-
- if self.scroll_timeline_axis.0[i] != Default::default() {
- dest.write_char(' ')?;
- self.scroll_timeline_axis.0[i].to_css(dest)?;
- }
-
- }
- Ok(())
- }
- }
-</%helpers:shorthand>
-
-// Note: view-timeline shorthand doesn't take view-timeline-inset into account.
-<%helpers:shorthand
- engines="gecko"
- name="view-timeline"
- sub_properties="view-timeline-name view-timeline-axis"
- gecko_pref="layout.css.scroll-driven-animations.enabled",
- spec="https://drafts.csswg.org/scroll-animations-1/#view-timeline-shorthand"
->
- pub fn parse_value<'i>(
- context: &ParserContext,
- input: &mut Parser<'i, '_>,
- ) -> Result<Longhands, ParseError<'i>> {
- use crate::properties::longhands::{view_timeline_axis, view_timeline_name};
-
- let mut names = Vec::with_capacity(1);
- let mut axes = Vec::with_capacity(1);
- input.parse_comma_separated(|input| {
- let name = view_timeline_name::single_value::parse(context, input)?;
- let axis = input.try_parse(|i| view_timeline_axis::single_value::parse(context, i));
-
- names.push(name);
- axes.push(axis.unwrap_or_default());
-
- Ok(())
- })?;
-
- Ok(expanded! {
- view_timeline_name: view_timeline_name::SpecifiedValue(names.into()),
- view_timeline_axis: view_timeline_axis::SpecifiedValue(axes.into()),
- })
- }
-
- impl<'a> ToCss for LonghandsToSerialize<'a> {
- fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result where W: fmt::Write {
- // If any value list length is differs then we don't do a shorthand serialization
- // either.
- let len = self.view_timeline_name.0.len();
- if len != self.view_timeline_axis.0.len() {
- return Ok(());
- }
-
- for i in 0..len {
- if i != 0 {
- dest.write_str(", ")?;
- }
-
- self.view_timeline_name.0[i].to_css(dest)?;
-
- if self.view_timeline_axis.0[i] != Default::default() {
- dest.write_char(' ')?;
- self.view_timeline_axis.0[i].to_css(dest)?;
- }
-
- }
- Ok(())
- }
- }
-</%helpers:shorthand>
diff --git a/components/style/properties_and_values/mod.rs b/components/style/properties_and_values/mod.rs
deleted file mode 100644
index 00f7ac98f75..00000000000
--- a/components/style/properties_and_values/mod.rs
+++ /dev/null
@@ -1,10 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
-
-//! Properties and Values
-//!
-//! https://drafts.css-houdini.org/css-properties-values-api-1/
-
-pub mod rule;
-pub mod syntax;
diff --git a/components/style/properties_and_values/rule.rs b/components/style/properties_and_values/rule.rs
deleted file mode 100644
index 916f7e93560..00000000000
--- a/components/style/properties_and_values/rule.rs
+++ /dev/null
@@ -1,245 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
-
-//! The [`@property`] at-rule.
-//!
-//! https://drafts.css-houdini.org/css-properties-values-api-1/#at-property-rule
-
-use crate::custom_properties::{Name as CustomPropertyName, SpecifiedValue};
-use crate::error_reporting::ContextualParseError;
-use crate::parser::{Parse, ParserContext};
-use crate::properties_and_values::syntax::Descriptor as SyntaxDescriptor;
-use crate::shared_lock::{SharedRwLockReadGuard, ToCssWithGuard};
-use crate::str::CssStringWriter;
-use crate::values::serialize_atom_name;
-use cssparser::{
- AtRuleParser, CowRcStr, DeclarationParser, ParseErrorKind, Parser, QualifiedRuleParser,
- RuleBodyItemParser, RuleBodyParser, SourceLocation,
-};
-#[cfg(feature = "gecko")]
-use malloc_size_of::{MallocSizeOf, MallocSizeOfOps};
-use selectors::parser::SelectorParseErrorKind;
-use servo_arc::Arc;
-use std::fmt::{self, Write};
-use style_traits::{CssWriter, ParseError, StyleParseErrorKind, ToCss};
-use to_shmem::{SharedMemoryBuilder, ToShmem};
-
-/// Parse the block inside a `@property` rule.
-///
-/// Valid `@property` rules result in a registered custom property, as if `registerProperty()` had
-/// been called with equivalent parameters.
-pub fn parse_property_block(
- context: &ParserContext,
- input: &mut Parser,
- name: PropertyRuleName,
- location: SourceLocation,
-) -> PropertyRuleData {
- let mut rule = PropertyRuleData::empty(name, location);
- let mut parser = PropertyRuleParser {
- context,
- rule: &mut rule,
- };
- let mut iter = RuleBodyParser::new(input, &mut parser);
- while let Some(declaration) = iter.next() {
- if !context.error_reporting_enabled() {
- continue;
- }
- if let Err((error, slice)) = declaration {
- let location = error.location;
- let error = if matches!(
- error.kind,
- ParseErrorKind::Custom(StyleParseErrorKind::PropertySyntaxField(_))
- ) {
- ContextualParseError::UnsupportedValue(slice, error)
- } else {
- ContextualParseError::UnsupportedPropertyDescriptor(slice, error)
- };
- context.log_css_error(location, error);
- }
- }
- rule
-}
-
-struct PropertyRuleParser<'a, 'b: 'a> {
- context: &'a ParserContext<'b>,
- rule: &'a mut PropertyRuleData,
-}
-
-/// Default methods reject all at rules.
-impl<'a, 'b, 'i> AtRuleParser<'i> for PropertyRuleParser<'a, 'b> {
- type Prelude = ();
- type AtRule = ();
- type Error = StyleParseErrorKind<'i>;
-}
-
-impl<'a, 'b, 'i> QualifiedRuleParser<'i> for PropertyRuleParser<'a, 'b> {
- type Prelude = ();
- type QualifiedRule = ();
- type Error = StyleParseErrorKind<'i>;
-}
-
-impl<'a, 'b, 'i> RuleBodyItemParser<'i, (), StyleParseErrorKind<'i>>
- for PropertyRuleParser<'a, 'b>
-{
- fn parse_qualified(&self) -> bool {
- false
- }
- fn parse_declarations(&self) -> bool {
- true
- }
-}
-
-macro_rules! property_descriptors {
- (
- $( #[$doc: meta] $name: tt $ident: ident: $ty: ty, )*
- ) => {
- /// Data inside a `@property` rule.
- ///
- /// <https://drafts.css-houdini.org/css-properties-values-api-1/#at-property-rule>
- #[derive(Clone, Debug, PartialEq)]
- pub struct PropertyRuleData {
- /// The custom property name.
- pub name: PropertyRuleName,
-
- $(
- #[$doc]
- pub $ident: Option<$ty>,
- )*
-
- /// Line and column of the @property rule source code.
- pub source_location: SourceLocation,
- }
-
- impl PropertyRuleData {
- /// Create an empty property rule
- pub fn empty(name: PropertyRuleName, source_location: SourceLocation) -> Self {
- PropertyRuleData {
- name,
- $(
- $ident: None,
- )*
- source_location,
- }
- }
-
- /// Serialization of declarations in PropertyRuleData
- pub fn decl_to_css(&self, dest: &mut CssStringWriter) -> fmt::Result {
- $(
- if let Some(ref value) = self.$ident {
- dest.write_str(concat!($name, ": "))?;
- value.to_css(&mut CssWriter::new(dest))?;
- dest.write_str("; ")?;
- }
- )*
- Ok(())
- }
- }
-
- impl<'a, 'b, 'i> DeclarationParser<'i> for PropertyRuleParser<'a, 'b> {
- type Declaration = ();
- type Error = StyleParseErrorKind<'i>;
-
- fn parse_value<'t>(
- &mut self,
- name: CowRcStr<'i>,
- input: &mut Parser<'i, 't>,
- ) -> Result<(), ParseError<'i>> {
- match_ignore_ascii_case! { &*name,
- $(
- $name => {
- // DeclarationParser also calls parse_entirely so we’d normally not need
- // to, but in this case we do because we set the value as a side effect
- // rather than returning it.
- let value = input.parse_entirely(|i| Parse::parse(self.context, i))?;
- self.rule.$ident = Some(value)
- },
- )*
- _ => return Err(input.new_custom_error(SelectorParseErrorKind::UnexpectedIdent(name.clone()))),
- }
- Ok(())
- }
- }
- }
-}
-
-property_descriptors! {
- /// <https://drafts.css-houdini.org/css-properties-values-api-1/#the-syntax-descriptor>
- "syntax" syntax: SyntaxDescriptor,
-
- /// <https://drafts.css-houdini.org/css-properties-values-api-1/#inherits-descriptor>
- "inherits" inherits: Inherits,
-
- /// <https://drafts.css-houdini.org/css-properties-values-api-1/#initial-value-descriptor>
- "initial-value" initial_value: InitialValue,
-}
-
-impl PropertyRuleData {
- /// Measure heap usage.
- #[cfg(feature = "gecko")]
- pub fn size_of(&self, _guard: &SharedRwLockReadGuard, ops: &mut MallocSizeOfOps) -> usize {
- self.name.0.size_of(ops) +
- self.syntax.size_of(ops) +
- self.inherits.size_of(ops) +
- if let Some(ref initial_value) = self.initial_value {
- initial_value.size_of(ops)
- } else {
- 0
- }
- }
-}
-
-impl ToCssWithGuard for PropertyRuleData {
- /// <https://drafts.css-houdini.org/css-properties-values-api-1/#serialize-a-csspropertyrule>
- fn to_css(&self, _guard: &SharedRwLockReadGuard, dest: &mut CssStringWriter) -> fmt::Result {
- dest.write_str("@property ")?;
- self.name.to_css(&mut CssWriter::new(dest))?;
- dest.write_str(" { ")?;
- self.decl_to_css(dest)?;
- dest.write_char('}')
- }
-}
-
-impl ToShmem for PropertyRuleData {
- fn to_shmem(&self, _builder: &mut SharedMemoryBuilder) -> to_shmem::Result<Self> {
- Err(String::from(
- "ToShmem failed for PropertyRule: cannot handle @property rules",
- ))
- }
-}
-
-/// A custom property name wrapper that includes the `--` prefix in its serialization
-#[derive(Clone, Debug, PartialEq)]
-pub struct PropertyRuleName(pub Arc<CustomPropertyName>);
-
-impl ToCss for PropertyRuleName {
- fn to_css<W: Write>(&self, dest: &mut CssWriter<W>) -> fmt::Result {
- dest.write_str("--")?;
- serialize_atom_name(&self.0, dest)
- }
-}
-
-/// <https://drafts.css-houdini.org/css-properties-values-api-1/#inherits-descriptor>
-#[derive(Clone, Debug, MallocSizeOf, Parse, PartialEq, ToCss)]
-pub enum Inherits {
- /// `true` value for the `inherits` descriptor
- True,
- /// `false` value for the `inherits` descriptor
- False,
-}
-
-/// Specifies the initial value of the custom property registration represented by the @property
-/// rule, controlling the property’s initial value.
-///
-/// The SpecifiedValue is wrapped in an Arc to avoid copying when using it.
-pub type InitialValue = Arc<SpecifiedValue>;
-
-impl Parse for InitialValue {
- fn parse<'i, 't>(
- _context: &ParserContext,
- input: &mut Parser<'i, 't>,
- ) -> Result<Self, ParseError<'i>> {
- input.skip_whitespace();
- SpecifiedValue::parse(input)
- }
-}
diff --git a/components/style/properties_and_values/syntax/ascii.rs b/components/style/properties_and_values/syntax/ascii.rs
deleted file mode 100644
index e1a1b08535b..00000000000
--- a/components/style/properties_and_values/syntax/ascii.rs
+++ /dev/null
@@ -1,60 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
-
-/// Trims ASCII whitespace characters from a slice, and returns the trimmed input.
-pub fn trim_ascii_whitespace(input: &str) -> &str {
- if input.is_empty() {
- return input;
- }
-
- let mut start = 0;
- {
- let mut iter = input.as_bytes().iter();
- loop {
- let byte = match iter.next() {
- Some(b) => b,
- None => return "",
- };
-
- if !byte.is_ascii_whitespace() {
- break;
- }
- start += 1;
- }
- }
-
- let mut end = input.len();
- assert!(start < end);
- {
- let mut iter = input.as_bytes()[start..].iter().rev();
- loop {
- let byte = match iter.next() {
- Some(b) => b,
- None => {
- debug_assert!(false, "We should have caught this in the loop above!");
- return "";
- },
- };
-
- if !byte.is_ascii_whitespace() {
- break;
- }
- end -= 1;
- }
- }
-
- &input[start..end]
-}
-
-#[test]
-fn trim_ascii_whitespace_test() {
- fn test(i: &str, o: &str) {
- assert_eq!(trim_ascii_whitespace(i), o)
- }
-
- test("", "");
- test(" ", "");
- test(" a b c ", "a b c");
- test(" \t \t \ta b c \t \t \t \t", "a b c");
-}
diff --git a/components/style/properties_and_values/syntax/data_type.rs b/components/style/properties_and_values/syntax/data_type.rs
deleted file mode 100644
index db5c1478db6..00000000000
--- a/components/style/properties_and_values/syntax/data_type.rs
+++ /dev/null
@@ -1,91 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
-
-use super::{Component, ComponentName, Multiplier};
-
-/// <https://drafts.css-houdini.org/css-properties-values-api-1/#supported-names>
-#[derive(Clone, Copy, Debug, MallocSizeOf, PartialEq)]
-pub enum DataType {
- /// Any valid `<length>` value
- Length,
- /// `<number>` values
- Number,
- /// Any valid <percentage> value
- Percentage,
- /// Any valid `<length>` or `<percentage>` value, any valid `<calc()>` expression combining
- /// `<length>` and `<percentage>` components.
- LengthPercentage,
- /// Any valid `<color>` value
- Color,
- /// Any valid `<image>` value
- Image,
- /// Any valid `<url>` value
- Url,
- /// Any valid `<integer>` value
- Integer,
- /// Any valid `<angle>` value
- Angle,
- /// Any valid `<time>` value
- Time,
- /// Any valid `<resolution>` value
- Resolution,
- /// Any valid `<transform-function>` value
- TransformFunction,
- /// A list of valid `<transform-function>` values. Note that "<transform-list>" is a pre-multiplied
- /// data type name equivalent to "<transform-function>+"
- TransformList,
- /// Any valid `<custom-ident>` value
- CustomIdent,
-}
-
-impl DataType {
- pub fn unpremultiply(&self) -> Option<Component> {
- match *self {
- DataType::TransformList => Some(Component {
- name: ComponentName::DataType(DataType::TransformFunction),
- multiplier: Some(Multiplier::Space),
- }),
- _ => None,
- }
- }
-
- pub fn from_str(ty: &str) -> Option<Self> {
- Some(match ty.as_bytes() {
- b"length" => DataType::Length,
- b"number" => DataType::Number,
- b"percentage" => DataType::Percentage,
- b"length-percentage" => DataType::LengthPercentage,
- b"color" => DataType::Color,
- b"image" => DataType::Image,
- b"url" => DataType::Url,
- b"integer" => DataType::Integer,
- b"angle" => DataType::Angle,
- b"time" => DataType::Time,
- b"resolution" => DataType::Resolution,
- b"transform-function" => DataType::TransformFunction,
- b"custom-ident" => DataType::CustomIdent,
- b"transform-list" => DataType::TransformList,
- _ => return None,
- })
- }
-
- pub fn to_str(&self) -> &str {
- match self {
- DataType::Length => "length",
- DataType::Number => "number",
- DataType::Percentage => "percentage",
- DataType::LengthPercentage => "length-percentage",
- DataType::Color => "color",
- DataType::Image => "image",
- DataType::Url => "url",
- DataType::Integer => "integer",
- DataType::Angle => "angle",
- DataType::Time => "time",
- DataType::Resolution => "resolution",
- DataType::TransformFunction => "transform-function",
- DataType::CustomIdent => "custom-ident",
- DataType::TransformList => "transform-list",
- }
- }
-}
diff --git a/components/style/properties_and_values/syntax/mod.rs b/components/style/properties_and_values/syntax/mod.rs
deleted file mode 100644
index 96f3c0ff4f3..00000000000
--- a/components/style/properties_and_values/syntax/mod.rs
+++ /dev/null
@@ -1,328 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
-
-//! Used for parsing and serializing the [`@property`] syntax string.
-//!
-//! <https://drafts.css-houdini.org/css-properties-values-api-1/#parsing-syntax>
-
-use std::fmt::{self, Debug};
-use std::{borrow::Cow, fmt::Write};
-
-use crate::parser::{Parse, ParserContext};
-use crate::values::CustomIdent;
-use cssparser::{Parser as CSSParser, ParserInput as CSSParserInput};
-use style_traits::{
- CssWriter, ParseError as StyleParseError, PropertySyntaxParseError as ParseError,
- StyleParseErrorKind, ToCss,
-};
-
-use self::data_type::DataType;
-
-mod ascii;
-mod data_type;
-
-/// <https://drafts.css-houdini.org/css-properties-values-api-1/#parsing-syntax>
-#[derive(Debug, Clone, MallocSizeOf)]
-pub struct Descriptor {
- components: Box<[Component]>,
- css: String,
-}
-
-impl Descriptor {
- /// Returns the universal syntax definition with the given CSS representation.
- fn universal(css: &str) -> Self {
- Self {
- components: Default::default(),
- css: String::from(css),
- }
- }
-
- /// Returns the specified syntax string.
- pub fn as_str(&self) -> &str {
- &self.css
- }
-}
-
-impl PartialEq for Descriptor {
- fn eq(&self, other: &Self) -> bool {
- self.components == other.components
- }
-}
-
-impl Parse for Descriptor {
- /// Parse a syntax descriptor.
- fn parse<'i, 't>(
- _context: &ParserContext,
- parser: &mut CSSParser<'i, 't>,
- ) -> Result<Self, StyleParseError<'i>> {
- // 1. Strip leading and trailing ASCII whitespace from string.
- let input = parser.expect_string()?;
- match parse_descriptor(input) {
- Ok(syntax) => Ok(syntax),
- Err(err) => Err(parser.new_custom_error(StyleParseErrorKind::PropertySyntaxField(err))),
- }
- }
-}
-
-impl ToCss for Descriptor {
- fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
- where
- W: Write,
- {
- self.css.to_css(dest)
- }
-}
-
-/// <https://drafts.css-houdini.org/css-properties-values-api-1/#multipliers>
-#[derive(Clone, Copy, Debug, MallocSizeOf, PartialEq)]
-pub enum Multiplier {
- /// Indicates a space-separated list.
- Space,
- /// Indicates a comma-separated list.
- Comma,
-}
-
-/// <https://drafts.css-houdini.org/css-properties-values-api-1/#syntax-component>
-#[derive(Clone, Debug, MallocSizeOf, PartialEq)]
-pub struct Component {
- name: ComponentName,
- multiplier: Option<Multiplier>,
-}
-
-impl Component {
- /// Returns the component's name.
- #[inline]
- pub fn name(&self) -> &ComponentName {
- &self.name
- }
-
- /// Returns the component's multiplier, if one exists.
- #[inline]
- pub fn multiplier(&self) -> Option<Multiplier> {
- self.multiplier
- }
-
- /// If the component is premultiplied, return the un-premultiplied component.
- #[inline]
- pub fn unpremultiplied(&self) -> Cow<Self> {
- match self.name.unpremultiply() {
- Some(component) => {
- debug_assert!(
- self.multiplier.is_none(),
- "Shouldn't have parsed a multiplier for a pre-multiplied data type name",
- );
- Cow::Owned(component)
- },
- None => Cow::Borrowed(self),
- }
- }
-}
-
-/// <https://drafts.css-houdini.org/css-properties-values-api-1/#syntax-component-name>
-#[derive(Clone, Debug, MallocSizeOf, PartialEq)]
-pub enum ComponentName {
- /// <https://drafts.css-houdini.org/css-properties-values-api-1/#data-type-name>
- DataType(DataType),
- /// <https://drafts.csswg.org/css-values-4/#custom-idents>
- Ident(CustomIdent),
-}
-
-impl ComponentName {
- fn unpremultiply(&self) -> Option<Component> {
- match *self {
- ComponentName::DataType(ref t) => t.unpremultiply(),
- ComponentName::Ident(..) => None,
- }
- }
-
- /// <https://drafts.css-houdini.org/css-properties-values-api-1/#pre-multiplied-data-type-name>
- fn is_pre_multiplied(&self) -> bool {
- self.unpremultiply().is_some()
- }
-}
-
-/// Parse a syntax descriptor.
-#[inline]
-fn parse_descriptor(css: &str) -> Result<Descriptor, ParseError> {
- // 1. Strip leading and trailing ASCII whitespace from string.
- let input = ascii::trim_ascii_whitespace(css);
-
- // 2. If string's length is 0, return failure.
- if input.is_empty() {
- return Err(ParseError::EmptyInput);
- }
-
- // 3. If string's length is 1, and the only code point in string is U+002A
- // ASTERISK (*), return the universal syntax descriptor.
- if input.len() == 1 && input.as_bytes()[0] == b'*' {
- return Ok(Descriptor::universal(css));
- }
-
- // 4. Let stream be an input stream created from the code points of string,
- // preprocessed as specified in [css-syntax-3]. Let descriptor be an
- // initially empty list of syntax components.
- //
- // NOTE(emilio): Instead of preprocessing we cheat and treat new-lines and
- // nulls in the parser specially.
- let mut components = vec![];
- {
- let mut parser = Parser::new(input, &mut components);
- // 5. Repeatedly consume the next input code point from stream.
- parser.parse()?;
- }
- Ok(Descriptor {
- components: components.into_boxed_slice(),
- css: String::from(css),
- })
-}
-
-struct Parser<'a> {
- input: &'a str,
- position: usize,
- output: &'a mut Vec<Component>,
-}
-
-/// <https://drafts.csswg.org/css-syntax-3/#whitespace>
-fn is_whitespace(byte: u8) -> bool {
- match byte {
- b'\t' | b'\n' | b'\r' | b' ' => true,
- _ => false,
- }
-}
-
-/// <https://drafts.csswg.org/css-syntax-3/#letter>
-fn is_letter(byte: u8) -> bool {
- match byte {
- b'A'..=b'Z' | b'a'..=b'z' => true,
- _ => false,
- }
-}
-
-/// <https://drafts.csswg.org/css-syntax-3/#non-ascii-code-point>
-fn is_non_ascii(byte: u8) -> bool {
- byte >= 0x80
-}
-
-/// <https://drafts.csswg.org/css-syntax-3/#name-start-code-point>
-fn is_name_start(byte: u8) -> bool {
- is_letter(byte) || is_non_ascii(byte) || byte == b'_'
-}
-
-impl<'a> Parser<'a> {
- fn new(input: &'a str, output: &'a mut Vec<Component>) -> Self {
- Self {
- input,
- position: 0,
- output,
- }
- }
-
- fn peek(&self) -> Option<u8> {
- self.input.as_bytes().get(self.position).cloned()
- }
-
- fn parse(&mut self) -> Result<(), ParseError> {
- // 5. Repeatedly consume the next input code point from stream:
- loop {
- let component = self.parse_component()?;
- self.output.push(component);
- self.skip_whitespace();
-
- let byte = match self.peek() {
- None => return Ok(()),
- Some(b) => b,
- };
-
- if byte != b'|' {
- return Err(ParseError::ExpectedPipeBetweenComponents);
- }
-
- self.position += 1;
- }
- }
-
- fn skip_whitespace(&mut self) {
- loop {
- match self.peek() {
- Some(c) if is_whitespace(c) => self.position += 1,
- _ => return,
- }
- }
- }
-
- /// <https://drafts.css-houdini.org/css-properties-values-api-1/#consume-data-type-name>
- fn parse_data_type_name(&mut self) -> Result<DataType, ParseError> {
- let start = self.position;
- loop {
- let byte = match self.peek() {
- Some(b) => b,
- None => return Err(ParseError::UnclosedDataTypeName),
- };
- if byte != b'>' {
- self.position += 1;
- continue;
- }
- let ty = match DataType::from_str(&self.input[start..self.position]) {
- Some(ty) => ty,
- None => return Err(ParseError::UnknownDataTypeName),
- };
- self.position += 1;
- return Ok(ty);
- }
- }
-
- fn parse_name(&mut self) -> Result<ComponentName, ParseError> {
- let b = match self.peek() {
- Some(b) => b,
- None => return Err(ParseError::UnexpectedEOF),
- };
-
- if b == b'<' {
- self.position += 1;
- return Ok(ComponentName::DataType(self.parse_data_type_name()?));
- }
-
- if b != b'\\' && !is_name_start(b) {
- return Err(ParseError::InvalidNameStart);
- }
-
- let input = &self.input[self.position..];
- let mut input = CSSParserInput::new(input);
- let mut input = CSSParser::new(&mut input);
- let location = input.current_source_location();
- let name = input
- .expect_ident()
- .ok()
- .and_then(|name| CustomIdent::from_ident(location, name, &[]).ok());
- let name = match name {
- Some(name) => name,
- None => return Err(ParseError::InvalidName),
- };
- self.position += input.position().byte_index();
- return Ok(ComponentName::Ident(name));
- }
-
- fn parse_multiplier(&mut self) -> Option<Multiplier> {
- let multiplier = match self.peek()? {
- b'+' => Multiplier::Space,
- b'#' => Multiplier::Comma,
- _ => return None,
- };
- self.position += 1;
- Some(multiplier)
- }
-
- /// <https://drafts.css-houdini.org/css-properties-values-api-1/#consume-a-syntax-component>
- fn parse_component(&mut self) -> Result<Component, ParseError> {
- // Consume as much whitespace as possible from stream.
- self.skip_whitespace();
- let name = self.parse_name()?;
- let multiplier = if name.is_pre_multiplied() {
- None
- } else {
- self.parse_multiplier()
- };
- Ok(Component { name, multiplier })
- }
-}
diff --git a/components/style/queries/condition.rs b/components/style/queries/condition.rs
deleted file mode 100644
index e17e6abd2e7..00000000000
--- a/components/style/queries/condition.rs
+++ /dev/null
@@ -1,366 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
-
-//! A query condition:
-//!
-//! https://drafts.csswg.org/mediaqueries-4/#typedef-media-condition
-//! https://drafts.csswg.org/css-contain-3/#typedef-container-condition
-
-use super::{FeatureFlags, FeatureType, QueryFeatureExpression};
-use crate::values::computed;
-use crate::{error_reporting::ContextualParseError, parser::ParserContext};
-use cssparser::{Parser, Token};
-use std::fmt::{self, Write};
-use style_traits::{CssWriter, ParseError, StyleParseErrorKind, ToCss};
-
-/// A binary `and` or `or` operator.
-#[derive(Clone, Copy, Debug, Eq, MallocSizeOf, Parse, PartialEq, ToCss, ToShmem)]
-#[allow(missing_docs)]
-pub enum Operator {
- And,
- Or,
-}
-
-/// Whether to allow an `or` condition or not during parsing.
-#[derive(Clone, Copy, Debug, Eq, MallocSizeOf, PartialEq, ToCss)]
-enum AllowOr {
- Yes,
- No,
-}
-
-/// https://en.wikipedia.org/wiki/Three-valued_logic#Kleene_and_Priest_logics
-#[derive(Clone, Copy, Debug, Eq, MallocSizeOf, PartialEq, ToCss)]
-pub enum KleeneValue {
- /// False
- False = 0,
- /// True
- True = 1,
- /// Either true or false, but we’re not sure which yet.
- Unknown,
-}
-
-impl From<bool> for KleeneValue {
- fn from(b: bool) -> Self {
- if b {
- Self::True
- } else {
- Self::False
- }
- }
-}
-
-impl KleeneValue {
- /// Turns this Kleene value to a bool, taking the unknown value as an
- /// argument.
- pub fn to_bool(self, unknown: bool) -> bool {
- match self {
- Self::True => true,
- Self::False => false,
- Self::Unknown => unknown,
- }
- }
-}
-
-impl std::ops::Not for KleeneValue {
- type Output = Self;
-
- fn not(self) -> Self {
- match self {
- Self::True => Self::False,
- Self::False => Self::True,
- Self::Unknown => Self::Unknown,
- }
- }
-}
-
-// Implements the logical and operation.
-impl std::ops::BitAnd for KleeneValue {
- type Output = Self;
-
- fn bitand(self, other: Self) -> Self {
- if self == Self::False || other == Self::False {
- return Self::False;
- }
- if self == Self::Unknown || other == Self::Unknown {
- return Self::Unknown;
- }
- Self::True
- }
-}
-
-// Implements the logical or operation.
-impl std::ops::BitOr for KleeneValue {
- type Output = Self;
-
- fn bitor(self, other: Self) -> Self {
- if self == Self::True || other == Self::True {
- return Self::True;
- }
- if self == Self::Unknown || other == Self::Unknown {
- return Self::Unknown;
- }
- Self::False
- }
-}
-
-impl std::ops::BitOrAssign for KleeneValue {
- fn bitor_assign(&mut self, other: Self) {
- *self = *self | other;
- }
-}
-
-impl std::ops::BitAndAssign for KleeneValue {
- fn bitand_assign(&mut self, other: Self) {
- *self = *self & other;
- }
-}
-
-/// Represents a condition.
-#[derive(Clone, Debug, MallocSizeOf, PartialEq, ToShmem)]
-pub enum QueryCondition {
- /// A simple feature expression, implicitly parenthesized.
- Feature(QueryFeatureExpression),
- /// A negation of a condition.
- Not(Box<QueryCondition>),
- /// A set of joint operations.
- Operation(Box<[QueryCondition]>, Operator),
- /// A condition wrapped in parenthesis.
- InParens(Box<QueryCondition>),
- /// [ <function-token> <any-value>? ) ] | [ ( <any-value>? ) ]
- GeneralEnclosed(String),
-}
-
-impl ToCss for QueryCondition {
- fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
- where
- W: fmt::Write,
- {
- match *self {
- // NOTE(emilio): QueryFeatureExpression already includes the
- // parenthesis.
- QueryCondition::Feature(ref f) => f.to_css(dest),
- QueryCondition::Not(ref c) => {
- dest.write_str("not ")?;
- c.to_css(dest)
- },
- QueryCondition::InParens(ref c) => {
- dest.write_char('(')?;
- c.to_css(dest)?;
- dest.write_char(')')
- },
- QueryCondition::Operation(ref list, op) => {
- let mut iter = list.iter();
- iter.next().unwrap().to_css(dest)?;
- for item in iter {
- dest.write_char(' ')?;
- op.to_css(dest)?;
- dest.write_char(' ')?;
- item.to_css(dest)?;
- }
- Ok(())
- },
- QueryCondition::GeneralEnclosed(ref s) => dest.write_str(&s),
- }
- }
-}
-
-/// <https://drafts.csswg.org/css-syntax-3/#typedef-any-value>
-fn consume_any_value<'i, 't>(input: &mut Parser<'i, 't>) -> Result<(), ParseError<'i>> {
- input.expect_no_error_token().map_err(Into::into)
-}
-
-impl QueryCondition {
- /// Parse a single condition.
- pub fn parse<'i, 't>(
- context: &ParserContext,
- input: &mut Parser<'i, 't>,
- feature_type: FeatureType,
- ) -> Result<Self, ParseError<'i>> {
- Self::parse_internal(context, input, feature_type, AllowOr::Yes)
- }
-
- fn visit<F>(&self, visitor: &mut F)
- where
- F: FnMut(&Self),
- {
- visitor(self);
- match *self {
- Self::Feature(..) => {},
- Self::GeneralEnclosed(..) => {},
- Self::Not(ref cond) => cond.visit(visitor),
- Self::Operation(ref conds, _op) => {
- for cond in conds.iter() {
- cond.visit(visitor);
- }
- },
- Self::InParens(ref cond) => cond.visit(visitor),
- }
- }
-
- /// Returns the union of all flags in the expression. This is useful for
- /// container queries.
- pub fn cumulative_flags(&self) -> FeatureFlags {
- let mut result = FeatureFlags::empty();
- self.visit(&mut |condition| {
- if let Self::Feature(ref f) = condition {
- result.insert(f.feature_flags())
- }
- });
- result
- }
-
- /// Parse a single condition, disallowing `or` expressions.
- ///
- /// To be used from the legacy query syntax.
- pub fn parse_disallow_or<'i, 't>(
- context: &ParserContext,
- input: &mut Parser<'i, 't>,
- feature_type: FeatureType,
- ) -> Result<Self, ParseError<'i>> {
- Self::parse_internal(context, input, feature_type, AllowOr::No)
- }
-
- /// https://drafts.csswg.org/mediaqueries-5/#typedef-media-condition or
- /// https://drafts.csswg.org/mediaqueries-5/#typedef-media-condition-without-or
- /// (depending on `allow_or`).
- fn parse_internal<'i, 't>(
- context: &ParserContext,
- input: &mut Parser<'i, 't>,
- feature_type: FeatureType,
- allow_or: AllowOr,
- ) -> Result<Self, ParseError<'i>> {
- let location = input.current_source_location();
- if input.try_parse(|i| i.expect_ident_matching("not")).is_ok() {
- let inner_condition = Self::parse_in_parens(context, input, feature_type)?;
- return Ok(QueryCondition::Not(Box::new(inner_condition)));
- }
-
- let first_condition = Self::parse_in_parens(context, input, feature_type)?;
- let operator = match input.try_parse(Operator::parse) {
- Ok(op) => op,
- Err(..) => return Ok(first_condition),
- };
-
- if allow_or == AllowOr::No && operator == Operator::Or {
- return Err(location.new_custom_error(StyleParseErrorKind::UnspecifiedError));
- }
-
- let mut conditions = vec![];
- conditions.push(first_condition);
- conditions.push(Self::parse_in_parens(context, input, feature_type)?);
-
- let delim = match operator {
- Operator::And => "and",
- Operator::Or => "or",
- };
-
- loop {
- if input.try_parse(|i| i.expect_ident_matching(delim)).is_err() {
- return Ok(QueryCondition::Operation(
- conditions.into_boxed_slice(),
- operator,
- ));
- }
-
- conditions.push(Self::parse_in_parens(context, input, feature_type)?);
- }
- }
-
- fn parse_in_parenthesis_block<'i>(
- context: &ParserContext,
- input: &mut Parser<'i, '_>,
- feature_type: FeatureType,
- ) -> Result<Self, ParseError<'i>> {
- // Base case. Make sure to preserve this error as it's more generally
- // relevant.
- let feature_error = match input.try_parse(|input| {
- QueryFeatureExpression::parse_in_parenthesis_block(context, input, feature_type)
- }) {
- Ok(expr) => return Ok(Self::Feature(expr)),
- Err(e) => e,
- };
- if let Ok(inner) = Self::parse(context, input, feature_type) {
- return Ok(Self::InParens(Box::new(inner)));
- }
- Err(feature_error)
- }
-
- /// Parse a condition in parentheses, or `<general-enclosed>`.
- ///
- /// https://drafts.csswg.org/mediaqueries/#typedef-media-in-parens
- pub fn parse_in_parens<'i, 't>(
- context: &ParserContext,
- input: &mut Parser<'i, 't>,
- feature_type: FeatureType,
- ) -> Result<Self, ParseError<'i>> {
- input.skip_whitespace();
- let start = input.position();
- let start_location = input.current_source_location();
- match *input.next()? {
- Token::ParenthesisBlock => {
- let nested = input.try_parse(|input| {
- input.parse_nested_block(|input| {
- Self::parse_in_parenthesis_block(context, input, feature_type)
- })
- });
- match nested {
- Ok(nested) => return Ok(nested),
- Err(e) => {
- // We're about to swallow the error in a `<general-enclosed>`
- // condition, so report it while we can.
- let loc = e.location;
- let error =
- ContextualParseError::InvalidMediaRule(input.slice_from(start), e);
- context.log_css_error(loc, error);
- },
- }
- },
- Token::Function(..) => {
- // TODO: handle `style()` queries, etc.
- },
- ref t => return Err(start_location.new_unexpected_token_error(t.clone())),
- }
- input.parse_nested_block(consume_any_value)?;
- Ok(Self::GeneralEnclosed(input.slice_from(start).to_owned()))
- }
-
- /// Whether this condition matches the device and quirks mode.
- /// https://drafts.csswg.org/mediaqueries/#evaluating
- /// https://drafts.csswg.org/mediaqueries/#typedef-general-enclosed
- /// Kleene 3-valued logic is adopted here due to the introduction of
- /// <general-enclosed>.
- pub fn matches(&self, context: &computed::Context) -> KleeneValue {
- match *self {
- QueryCondition::Feature(ref f) => f.matches(context),
- QueryCondition::GeneralEnclosed(_) => KleeneValue::Unknown,
- QueryCondition::InParens(ref c) => c.matches(context),
- QueryCondition::Not(ref c) => !c.matches(context),
- QueryCondition::Operation(ref conditions, op) => {
- debug_assert!(!conditions.is_empty(), "We never create an empty op");
- match op {
- Operator::And => {
- let mut result = KleeneValue::True;
- for c in conditions.iter() {
- result &= c.matches(context);
- if result == KleeneValue::False {
- break;
- }
- }
- result
- },
- Operator::Or => {
- let mut result = KleeneValue::False;
- for c in conditions.iter() {
- result |= c.matches(context);
- if result == KleeneValue::True {
- break;
- }
- }
- result
- },
- }
- },
- }
- }
-}
diff --git a/components/style/queries/feature.rs b/components/style/queries/feature.rs
deleted file mode 100644
index e8fd62284bd..00000000000
--- a/components/style/queries/feature.rs
+++ /dev/null
@@ -1,195 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
-
-//! Query features.
-
-use super::condition::KleeneValue;
-use crate::parser::ParserContext;
-use crate::values::computed::{self, CSSPixelLength, Ratio, Resolution};
-use crate::Atom;
-use cssparser::Parser;
-use std::fmt;
-use style_traits::ParseError;
-
-/// A generic discriminant for an enum value.
-pub type KeywordDiscriminant = u8;
-
-type QueryFeatureGetter<T> = fn(device: &computed::Context) -> T;
-
-/// Serializes a given discriminant.
-///
-/// FIXME(emilio): we could prevent this allocation if the ToCss code would
-/// generate a method for keywords to get the static string or something.
-pub type KeywordSerializer = fn(KeywordDiscriminant) -> String;
-
-/// Parses a given identifier.
-pub type KeywordParser = for<'a, 'i, 't> fn(
- context: &'a ParserContext,
- input: &'a mut Parser<'i, 't>,
-) -> Result<KeywordDiscriminant, ParseError<'i>>;
-
-/// An evaluator for a given feature.
-///
-/// This determines the kind of values that get parsed, too.
-#[allow(missing_docs)]
-pub enum Evaluator {
- Length(QueryFeatureGetter<CSSPixelLength>),
- OptionalLength(QueryFeatureGetter<Option<CSSPixelLength>>),
- Integer(QueryFeatureGetter<i32>),
- Float(QueryFeatureGetter<f32>),
- BoolInteger(QueryFeatureGetter<bool>),
- /// A non-negative number ratio, such as the one from device-pixel-ratio.
- NumberRatio(QueryFeatureGetter<Ratio>),
- OptionalNumberRatio(QueryFeatureGetter<Option<Ratio>>),
- /// A resolution.
- Resolution(QueryFeatureGetter<Resolution>),
- /// A keyword value.
- Enumerated {
- /// The parser to get a discriminant given a string.
- parser: KeywordParser,
- /// The serializer to get a string from a discriminant.
- ///
- /// This is guaranteed to be called with a keyword that `parser` has
- /// produced.
- serializer: KeywordSerializer,
- /// The evaluator itself. This is guaranteed to be called with a
- /// keyword that `parser` has produced.
- evaluator: fn(&computed::Context, Option<KeywordDiscriminant>) -> KleeneValue,
- },
-}
-
-/// A simple helper macro to create a keyword evaluator.
-///
-/// This assumes that keyword feature expressions don't accept ranges, and
-/// asserts if that's not true. As of today there's nothing like that (does that
-/// even make sense?).
-macro_rules! keyword_evaluator {
- ($actual_evaluator:ident, $keyword_type:ty) => {{
- fn __parse<'i, 't>(
- context: &$crate::parser::ParserContext,
- input: &mut $crate::cssparser::Parser<'i, 't>,
- ) -> Result<$crate::queries::feature::KeywordDiscriminant, ::style_traits::ParseError<'i>>
- {
- let kw = <$keyword_type as $crate::parser::Parse>::parse(context, input)?;
- Ok(kw as $crate::queries::feature::KeywordDiscriminant)
- }
-
- fn __serialize(kw: $crate::queries::feature::KeywordDiscriminant) -> String {
- // This unwrap is ok because the only discriminants that get
- // back to us is the ones that `parse` produces.
- let value: $keyword_type = ::num_traits::cast::FromPrimitive::from_u8(kw).unwrap();
- <$keyword_type as ::style_traits::ToCss>::to_css_string(&value)
- }
-
- fn __evaluate(
- context: &$crate::values::computed::Context,
- value: Option<$crate::queries::feature::KeywordDiscriminant>,
- ) -> $crate::queries::condition::KleeneValue {
- // This unwrap is ok because the only discriminants that get
- // back to us is the ones that `parse` produces.
- let value: Option<$keyword_type> =
- value.map(|kw| ::num_traits::cast::FromPrimitive::from_u8(kw).unwrap());
- $crate::queries::condition::KleeneValue::from($actual_evaluator(context, value))
- }
-
- $crate::queries::feature::Evaluator::Enumerated {
- parser: __parse,
- serializer: __serialize,
- evaluator: __evaluate,
- }
- }};
-}
-
-bitflags! {
- /// Different flags or toggles that change how a expression is parsed or
- /// evaluated.
- #[derive(ToShmem)]
- pub struct FeatureFlags : u8 {
- /// The feature should only be parsed in chrome and ua sheets.
- const CHROME_AND_UA_ONLY = 1 << 0;
- /// The feature requires a -webkit- prefix.
- const WEBKIT_PREFIX = 1 << 1;
- /// The feature requires the inline-axis containment.
- const CONTAINER_REQUIRES_INLINE_AXIS = 1 << 2;
- /// The feature requires the block-axis containment.
- const CONTAINER_REQUIRES_BLOCK_AXIS = 1 << 3;
- /// The feature requires containment in the physical width axis.
- const CONTAINER_REQUIRES_WIDTH_AXIS = 1 << 4;
- /// The feature requires containment in the physical height axis.
- const CONTAINER_REQUIRES_HEIGHT_AXIS = 1 << 5;
- /// The feature evaluation depends on the viewport size.
- const VIEWPORT_DEPENDENT = 1 << 6;
- }
-}
-
-impl FeatureFlags {
- /// Returns parsing requirement flags.
- pub fn parsing_requirements(self) -> Self {
- self.intersection(Self::CHROME_AND_UA_ONLY | Self::WEBKIT_PREFIX)
- }
-
- /// Returns all the container axis flags.
- pub fn all_container_axes() -> Self {
- Self::CONTAINER_REQUIRES_INLINE_AXIS |
- Self::CONTAINER_REQUIRES_BLOCK_AXIS |
- Self::CONTAINER_REQUIRES_WIDTH_AXIS |
- Self::CONTAINER_REQUIRES_HEIGHT_AXIS
- }
-
- /// Returns our subset of container axis flags.
- pub fn container_axes(self) -> Self {
- self.intersection(Self::all_container_axes())
- }
-}
-
-/// Whether a feature allows ranges or not.
-#[derive(Clone, Copy, Debug, Eq, PartialEq)]
-#[allow(missing_docs)]
-pub enum AllowsRanges {
- Yes,
- No,
-}
-
-/// A description of a feature.
-pub struct QueryFeatureDescription {
- /// The feature name, in ascii lowercase.
- pub name: Atom,
- /// Whether min- / max- prefixes are allowed or not.
- pub allows_ranges: AllowsRanges,
- /// The evaluator, which we also use to determine which kind of value to
- /// parse.
- pub evaluator: Evaluator,
- /// Different feature-specific flags.
- pub flags: FeatureFlags,
-}
-
-impl QueryFeatureDescription {
- /// Whether this feature allows ranges.
- #[inline]
- pub fn allows_ranges(&self) -> bool {
- self.allows_ranges == AllowsRanges::Yes
- }
-}
-
-/// A simple helper to construct a `QueryFeatureDescription`.
-macro_rules! feature {
- ($name:expr, $allows_ranges:expr, $evaluator:expr, $flags:expr,) => {
- $crate::queries::feature::QueryFeatureDescription {
- name: $name,
- allows_ranges: $allows_ranges,
- evaluator: $evaluator,
- flags: $flags,
- }
- };
-}
-
-impl fmt::Debug for QueryFeatureDescription {
- fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
- f.debug_struct("QueryFeatureDescription")
- .field("name", &self.name)
- .field("allows_ranges", &self.allows_ranges)
- .field("flags", &self.flags)
- .finish()
- }
-}
diff --git a/components/style/queries/feature_expression.rs b/components/style/queries/feature_expression.rs
deleted file mode 100644
index a3f067c923a..00000000000
--- a/components/style/queries/feature_expression.rs
+++ /dev/null
@@ -1,754 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
-
-//! Parsing for query feature expressions, like `(foo: bar)` or
-//! `(width >= 400px)`.
-
-use super::feature::{Evaluator, QueryFeatureDescription};
-use super::feature::{FeatureFlags, KeywordDiscriminant};
-use crate::parser::{Parse, ParserContext};
-use crate::queries::condition::KleeneValue;
-use crate::str::{starts_with_ignore_ascii_case, string_as_ascii_lowercase};
-use crate::values::computed::{self, Ratio, ToComputedValue};
-use crate::values::specified::{Integer, Length, Number, Resolution};
-use crate::values::CSSFloat;
-use crate::{Atom, Zero};
-use cssparser::{Parser, Token};
-use std::cmp::{Ordering, PartialOrd};
-use std::fmt::{self, Write};
-use style_traits::{CssWriter, ParseError, StyleParseErrorKind, ToCss};
-
-/// Whether we're parsing a media or container query feature.
-#[derive(Clone, Copy, Debug, Eq, MallocSizeOf, PartialEq, ToShmem)]
-pub enum FeatureType {
- /// We're parsing a media feature.
- Media,
- /// We're parsing a container feature.
- Container,
-}
-
-impl FeatureType {
- fn features(&self) -> &'static [QueryFeatureDescription] {
- #[cfg(feature = "gecko")]
- let media_features = &crate::gecko::media_features::MEDIA_FEATURES;
- #[cfg(feature = "servo")]
- let media_features = &*crate::servo::media_queries::MEDIA_FEATURES;
-
- use crate::stylesheets::container_rule::CONTAINER_FEATURES;
-
- match *self {
- FeatureType::Media => media_features,
- FeatureType::Container => &CONTAINER_FEATURES,
- }
- }
-
- fn find_feature(&self, name: &Atom) -> Option<(usize, &'static QueryFeatureDescription)> {
- self.features()
- .iter()
- .enumerate()
- .find(|(_, f)| f.name == *name)
- }
-}
-
-/// The kind of matching that should be performed on a feature value.
-#[derive(Clone, Copy, Debug, Eq, MallocSizeOf, PartialEq, ToShmem)]
-enum LegacyRange {
- /// At least the specified value.
- Min,
- /// At most the specified value.
- Max,
-}
-
-/// The operator that was specified in this feature.
-#[derive(Clone, Copy, Debug, Eq, MallocSizeOf, PartialEq, ToShmem)]
-enum Operator {
- /// =
- Equal,
- /// >
- GreaterThan,
- /// >=
- GreaterThanEqual,
- /// <
- LessThan,
- /// <=
- LessThanEqual,
-}
-
-impl ToCss for Operator {
- fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
- where
- W: fmt::Write,
- {
- dest.write_str(match *self {
- Self::Equal => "=",
- Self::LessThan => "<",
- Self::LessThanEqual => "<=",
- Self::GreaterThan => ">",
- Self::GreaterThanEqual => ">=",
- })
- }
-}
-
-impl Operator {
- fn is_compatible_with(self, right_op: Self) -> bool {
- // Some operators are not compatible with each other in multi-range
- // context.
- match self {
- Self::Equal => false,
- Self::GreaterThan | Self::GreaterThanEqual => {
- matches!(right_op, Self::GreaterThan | Self::GreaterThanEqual)
- },
- Self::LessThan | Self::LessThanEqual => {
- matches!(right_op, Self::LessThan | Self::LessThanEqual)
- },
- }
- }
-
- fn evaluate(&self, cmp: Ordering) -> bool {
- match *self {
- Self::Equal => cmp == Ordering::Equal,
- Self::GreaterThan => cmp == Ordering::Greater,
- Self::GreaterThanEqual => cmp == Ordering::Equal || cmp == Ordering::Greater,
- Self::LessThan => cmp == Ordering::Less,
- Self::LessThanEqual => cmp == Ordering::Equal || cmp == Ordering::Less,
- }
- }
-
- fn parse<'i>(input: &mut Parser<'i, '_>) -> Result<Self, ParseError<'i>> {
- let location = input.current_source_location();
- let operator = match *input.next()? {
- Token::Delim('=') => return Ok(Operator::Equal),
- Token::Delim('>') => Operator::GreaterThan,
- Token::Delim('<') => Operator::LessThan,
- ref t => return Err(location.new_unexpected_token_error(t.clone())),
- };
-
- // https://drafts.csswg.org/mediaqueries-4/#mq-syntax:
- //
- // No whitespace is allowed between the “<” or “>”
- // <delim-token>s and the following “=” <delim-token>, if it’s
- // present.
- //
- // TODO(emilio): Maybe we should ignore comments as well?
- // https://github.com/w3c/csswg-drafts/issues/6248
- let parsed_equal = input
- .try_parse(|i| {
- let t = i.next_including_whitespace().map_err(|_| ())?;
- if !matches!(t, Token::Delim('=')) {
- return Err(());
- }
- Ok(())
- })
- .is_ok();
-
- if !parsed_equal {
- return Ok(operator);
- }
-
- Ok(match operator {
- Operator::GreaterThan => Operator::GreaterThanEqual,
- Operator::LessThan => Operator::LessThanEqual,
- _ => unreachable!(),
- })
- }
-}
-
-#[derive(Clone, Debug, MallocSizeOf, ToShmem, PartialEq)]
-enum QueryFeatureExpressionKind {
- /// Just the media feature name.
- Empty,
-
- /// A single value.
- Single(QueryExpressionValue),
-
- /// Legacy range syntax (min-*: value) or so.
- LegacyRange(LegacyRange, QueryExpressionValue),
-
- /// Modern range context syntax:
- /// https://drafts.csswg.org/mediaqueries-5/#mq-range-context
- Range {
- left: Option<(Operator, QueryExpressionValue)>,
- right: Option<(Operator, QueryExpressionValue)>,
- },
-}
-
-impl QueryFeatureExpressionKind {
- /// Evaluate a given range given an optional query value and a value from
- /// the browser.
- fn evaluate<T>(
- &self,
- context_value: T,
- mut compute: impl FnMut(&QueryExpressionValue) -> T,
- ) -> bool
- where
- T: PartialOrd + Zero,
- {
- match *self {
- Self::Empty => return !context_value.is_zero(),
- Self::Single(ref value) => {
- let value = compute(value);
- let cmp = match context_value.partial_cmp(&value) {
- Some(c) => c,
- None => return false,
- };
- cmp == Ordering::Equal
- },
- Self::LegacyRange(ref range, ref value) => {
- let value = compute(value);
- let cmp = match context_value.partial_cmp(&value) {
- Some(c) => c,
- None => return false,
- };
- cmp == Ordering::Equal ||
- match range {
- LegacyRange::Min => cmp == Ordering::Greater,
- LegacyRange::Max => cmp == Ordering::Less,
- }
- },
- Self::Range {
- ref left,
- ref right,
- } => {
- debug_assert!(left.is_some() || right.is_some());
- if let Some((ref op, ref value)) = left {
- let value = compute(value);
- let cmp = match value.partial_cmp(&context_value) {
- Some(c) => c,
- None => return false,
- };
- if !op.evaluate(cmp) {
- return false;
- }
- }
- if let Some((ref op, ref value)) = right {
- let value = compute(value);
- let cmp = match context_value.partial_cmp(&value) {
- Some(c) => c,
- None => return false,
- };
- if !op.evaluate(cmp) {
- return false;
- }
- }
- true
- },
- }
- }
-
- /// Non-ranged features only need to compare to one value at most.
- fn non_ranged_value(&self) -> Option<&QueryExpressionValue> {
- match *self {
- Self::Empty => None,
- Self::Single(ref v) => Some(v),
- Self::LegacyRange(..) | Self::Range { .. } => {
- debug_assert!(false, "Unexpected ranged value in non-ranged feature!");
- None
- },
- }
- }
-}
-
-/// A feature expression contains a reference to the feature, the value the
-/// query contained, and the range to evaluate.
-#[derive(Clone, Debug, MallocSizeOf, ToShmem, PartialEq)]
-pub struct QueryFeatureExpression {
- feature_type: FeatureType,
- feature_index: usize,
- kind: QueryFeatureExpressionKind,
-}
-
-impl ToCss for QueryFeatureExpression {
- fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
- where
- W: fmt::Write,
- {
- dest.write_char('(')?;
-
- match self.kind {
- QueryFeatureExpressionKind::Empty => self.write_name(dest)?,
- QueryFeatureExpressionKind::Single(ref v) |
- QueryFeatureExpressionKind::LegacyRange(_, ref v) => {
- self.write_name(dest)?;
- dest.write_str(": ")?;
- v.to_css(dest, self)?;
- },
- QueryFeatureExpressionKind::Range {
- ref left,
- ref right,
- } => {
- if let Some((ref op, ref val)) = left {
- val.to_css(dest, self)?;
- dest.write_char(' ')?;
- op.to_css(dest)?;
- dest.write_char(' ')?;
- }
- self.write_name(dest)?;
- if let Some((ref op, ref val)) = right {
- dest.write_char(' ')?;
- op.to_css(dest)?;
- dest.write_char(' ')?;
- val.to_css(dest, self)?;
- }
- },
- }
- dest.write_char(')')
- }
-}
-
-fn consume_operation_or_colon<'i>(
- input: &mut Parser<'i, '_>,
-) -> Result<Option<Operator>, ParseError<'i>> {
- if input.try_parse(|input| input.expect_colon()).is_ok() {
- return Ok(None);
- }
- Operator::parse(input).map(|op| Some(op))
-}
-
-#[allow(unused_variables)]
-fn disabled_by_pref(feature: &Atom, context: &ParserContext) -> bool {
- #[cfg(feature = "gecko")]
- {
- if *feature == atom!("forced-colors") {
- // forced-colors is always enabled in the ua and chrome. On
- // the web it is hidden behind a preference, which is defaulted
- // to 'true' as of bug 1659511.
- return !context.in_ua_or_chrome_sheet() &&
- !static_prefs::pref!("layout.css.forced-colors.enabled");
- }
- // prefers-contrast is always enabled in the ua and chrome. On
- // the web it is hidden behind a preference.
- if *feature == atom!("prefers-contrast") {
- return !context.in_ua_or_chrome_sheet() &&
- !static_prefs::pref!("layout.css.prefers-contrast.enabled");
- }
-
- // prefers-reduced-transparency is always enabled in the ua and chrome. On
- // the web it is hidden behind a preference (see Bug 1822176).
- if *feature == atom!("prefers-reduced-transparency") {
- return !context.in_ua_or_chrome_sheet() &&
- !static_prefs::pref!("layout.css.prefers-reduced-transparency.enabled");
- }
-
- // inverted-colors is always enabled in the ua and chrome. On
- // the web it is hidden behind a preferenc.
- if *feature == atom!("inverted-colors") {
- return !context.in_ua_or_chrome_sheet() &&
- !static_prefs::pref!("layout.css.inverted-colors.enabled");
- }
- }
- false
-}
-
-impl QueryFeatureExpression {
- fn new(
- feature_type: FeatureType,
- feature_index: usize,
- kind: QueryFeatureExpressionKind,
- ) -> Self {
- debug_assert!(feature_index < feature_type.features().len());
- Self {
- feature_type,
- feature_index,
- kind,
- }
- }
-
- fn write_name<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
- where
- W: fmt::Write,
- {
- let feature = self.feature();
- if feature.flags.contains(FeatureFlags::WEBKIT_PREFIX) {
- dest.write_str("-webkit-")?;
- }
-
- if let QueryFeatureExpressionKind::LegacyRange(range, _) = self.kind {
- match range {
- LegacyRange::Min => dest.write_str("min-")?,
- LegacyRange::Max => dest.write_str("max-")?,
- }
- }
-
- // NB: CssStringWriter not needed, feature names are under control.
- write!(dest, "{}", feature.name)?;
-
- Ok(())
- }
-
- fn feature(&self) -> &'static QueryFeatureDescription {
- &self.feature_type.features()[self.feature_index]
- }
-
- /// Returns the feature flags for our feature.
- pub fn feature_flags(&self) -> FeatureFlags {
- self.feature().flags
- }
-
- /// Parse a feature expression of the form:
- ///
- /// ```
- /// (media-feature: media-value)
- /// ```
- pub fn parse<'i, 't>(
- context: &ParserContext,
- input: &mut Parser<'i, 't>,
- feature_type: FeatureType,
- ) -> Result<Self, ParseError<'i>> {
- input.expect_parenthesis_block()?;
- input.parse_nested_block(|input| {
- Self::parse_in_parenthesis_block(context, input, feature_type)
- })
- }
-
- fn parse_feature_name<'i, 't>(
- context: &ParserContext,
- input: &mut Parser<'i, 't>,
- feature_type: FeatureType,
- ) -> Result<(usize, Option<LegacyRange>), ParseError<'i>> {
- let mut flags = FeatureFlags::empty();
- let location = input.current_source_location();
- let ident = input.expect_ident()?;
-
- if context.in_ua_or_chrome_sheet() {
- flags.insert(FeatureFlags::CHROME_AND_UA_ONLY);
- }
-
- let mut feature_name = &**ident;
- if starts_with_ignore_ascii_case(feature_name, "-webkit-") {
- feature_name = &feature_name[8..];
- flags.insert(FeatureFlags::WEBKIT_PREFIX);
- }
-
- let range = if starts_with_ignore_ascii_case(feature_name, "min-") {
- feature_name = &feature_name[4..];
- Some(LegacyRange::Min)
- } else if starts_with_ignore_ascii_case(feature_name, "max-") {
- feature_name = &feature_name[4..];
- Some(LegacyRange::Max)
- } else {
- None
- };
-
- let atom = Atom::from(string_as_ascii_lowercase(feature_name));
- let (feature_index, feature) = match feature_type.find_feature(&atom) {
- Some((i, f)) => (i, f),
- None => {
- return Err(location.new_custom_error(
- StyleParseErrorKind::MediaQueryExpectedFeatureName(ident.clone()),
- ))
- },
- };
-
- if disabled_by_pref(&feature.name, context) ||
- !flags.contains(feature.flags.parsing_requirements()) ||
- (range.is_some() && !feature.allows_ranges())
- {
- return Err(location.new_custom_error(
- StyleParseErrorKind::MediaQueryExpectedFeatureName(ident.clone()),
- ));
- }
-
- Ok((feature_index, range))
- }
-
- /// Parses the following range syntax:
- ///
- /// (feature-value <operator> feature-name)
- /// (feature-value <operator> feature-name <operator> feature-value)
- fn parse_multi_range_syntax<'i, 't>(
- context: &ParserContext,
- input: &mut Parser<'i, 't>,
- feature_type: FeatureType,
- ) -> Result<Self, ParseError<'i>> {
- let start = input.state();
-
- // To parse the values, we first need to find the feature name. We rely
- // on feature values for ranged features not being able to be top-level
- // <ident>s, which holds.
- let feature_index = loop {
- // NOTE: parse_feature_name advances the input.
- if let Ok((index, range)) = Self::parse_feature_name(context, input, feature_type) {
- if range.is_some() {
- // Ranged names are not allowed here.
- return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError));
- }
- break index;
- }
- if input.is_exhausted() {
- return Err(start
- .source_location()
- .new_custom_error(StyleParseErrorKind::UnspecifiedError));
- }
- };
-
- input.reset(&start);
-
- let feature = &feature_type.features()[feature_index];
- let left_val = QueryExpressionValue::parse(feature, context, input)?;
- let left_op = Operator::parse(input)?;
-
- {
- let (parsed_index, _) = Self::parse_feature_name(context, input, feature_type)?;
- debug_assert_eq!(
- parsed_index, feature_index,
- "How did we find a different feature?"
- );
- }
-
- let right_op = input.try_parse(Operator::parse).ok();
- let right = match right_op {
- Some(op) => {
- if !left_op.is_compatible_with(op) {
- return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError));
- }
- Some((op, QueryExpressionValue::parse(feature, context, input)?))
- },
- None => None,
- };
- Ok(Self::new(
- feature_type,
- feature_index,
- QueryFeatureExpressionKind::Range {
- left: Some((left_op, left_val)),
- right,
- },
- ))
- }
-
- /// Parse a feature expression where we've already consumed the parenthesis.
- pub fn parse_in_parenthesis_block<'i, 't>(
- context: &ParserContext,
- input: &mut Parser<'i, 't>,
- feature_type: FeatureType,
- ) -> Result<Self, ParseError<'i>> {
- let (feature_index, range) =
- match input.try_parse(|input| Self::parse_feature_name(context, input, feature_type)) {
- Ok(v) => v,
- Err(e) => {
- if let Ok(expr) = Self::parse_multi_range_syntax(context, input, feature_type) {
- return Ok(expr);
- }
- return Err(e);
- },
- };
- let operator = input.try_parse(consume_operation_or_colon);
- let operator = match operator {
- Err(..) => {
- // If there's no colon, this is a query of the form
- // '(<feature>)', that is, there's no value specified.
- //
- // Gecko doesn't allow ranged expressions without a
- // value, so just reject them here too.
- if range.is_some() {
- return Err(
- input.new_custom_error(StyleParseErrorKind::RangedExpressionWithNoValue)
- );
- }
-
- return Ok(Self::new(
- feature_type,
- feature_index,
- QueryFeatureExpressionKind::Empty,
- ));
- },
- Ok(operator) => operator,
- };
-
- let feature = &feature_type.features()[feature_index];
-
- let value = QueryExpressionValue::parse(feature, context, input).map_err(|err| {
- err.location
- .new_custom_error(StyleParseErrorKind::MediaQueryExpectedFeatureValue)
- })?;
-
- let kind = match range {
- Some(range) => {
- if operator.is_some() {
- return Err(
- input.new_custom_error(StyleParseErrorKind::MediaQueryUnexpectedOperator)
- );
- }
- QueryFeatureExpressionKind::LegacyRange(range, value)
- },
- None => match operator {
- Some(operator) => {
- if !feature.allows_ranges() {
- return Err(input
- .new_custom_error(StyleParseErrorKind::MediaQueryUnexpectedOperator));
- }
- QueryFeatureExpressionKind::Range {
- left: None,
- right: Some((operator, value)),
- }
- },
- None => QueryFeatureExpressionKind::Single(value),
- },
- };
-
- Ok(Self::new(feature_type, feature_index, kind))
- }
-
- /// Returns whether this query evaluates to true for the given device.
- pub fn matches(&self, context: &computed::Context) -> KleeneValue {
- macro_rules! expect {
- ($variant:ident, $v:expr) => {
- match *$v {
- QueryExpressionValue::$variant(ref v) => v,
- _ => unreachable!("Unexpected QueryExpressionValue"),
- }
- };
- }
-
- KleeneValue::from(match self.feature().evaluator {
- Evaluator::Length(eval) => {
- let v = eval(context);
- self.kind
- .evaluate(v, |v| expect!(Length, v).to_computed_value(context))
- },
- Evaluator::OptionalLength(eval) => {
- let v = match eval(context) {
- Some(v) => v,
- None => return KleeneValue::Unknown,
- };
- self.kind
- .evaluate(v, |v| expect!(Length, v).to_computed_value(context))
- },
- Evaluator::Integer(eval) => {
- let v = eval(context);
- self.kind.evaluate(v, |v| *expect!(Integer, v))
- },
- Evaluator::Float(eval) => {
- let v = eval(context);
- self.kind.evaluate(v, |v| *expect!(Float, v))
- },
- Evaluator::NumberRatio(eval) => {
- let ratio = eval(context);
- // A ratio of 0/0 behaves as the ratio 1/0, so we need to call used_value()
- // to convert it if necessary.
- // FIXME: we may need to update here once
- // https://github.com/w3c/csswg-drafts/issues/4954 got resolved.
- self.kind
- .evaluate(ratio, |v| expect!(NumberRatio, v).used_value())
- },
- Evaluator::OptionalNumberRatio(eval) => {
- let ratio = match eval(context) {
- Some(v) => v,
- None => return KleeneValue::Unknown,
- };
- // See above for subtleties here.
- self.kind
- .evaluate(ratio, |v| expect!(NumberRatio, v).used_value())
- },
- Evaluator::Resolution(eval) => {
- let v = eval(context).dppx();
- self.kind.evaluate(v, |v| {
- expect!(Resolution, v).to_computed_value(context).dppx()
- })
- },
- Evaluator::Enumerated { evaluator, .. } => {
- let computed = self
- .kind
- .non_ranged_value()
- .map(|v| *expect!(Enumerated, v));
- return evaluator(context, computed);
- },
- Evaluator::BoolInteger(eval) => {
- let computed = self
- .kind
- .non_ranged_value()
- .map(|v| *expect!(BoolInteger, v));
- let boolean = eval(context);
- computed.map_or(boolean, |v| v == boolean)
- },
- })
- }
-}
-
-/// A value found or expected in a expression.
-///
-/// FIXME(emilio): How should calc() serialize in the Number / Integer /
-/// BoolInteger / NumberRatio case, as computed or as specified value?
-///
-/// If the first, this would need to store the relevant values.
-///
-/// See: https://github.com/w3c/csswg-drafts/issues/1968
-#[derive(Clone, Debug, MallocSizeOf, PartialEq, ToShmem)]
-pub enum QueryExpressionValue {
- /// A length.
- Length(Length),
- /// An integer.
- Integer(i32),
- /// A floating point value.
- Float(CSSFloat),
- /// A boolean value, specified as an integer (i.e., either 0 or 1).
- BoolInteger(bool),
- /// A single non-negative number or two non-negative numbers separated by '/',
- /// with optional whitespace on either side of the '/'.
- NumberRatio(Ratio),
- /// A resolution.
- Resolution(Resolution),
- /// An enumerated value, defined by the variant keyword table in the
- /// feature's `mData` member.
- Enumerated(KeywordDiscriminant),
-}
-
-impl QueryExpressionValue {
- fn to_css<W>(&self, dest: &mut CssWriter<W>, for_expr: &QueryFeatureExpression) -> fmt::Result
- where
- W: fmt::Write,
- {
- match *self {
- QueryExpressionValue::Length(ref l) => l.to_css(dest),
- QueryExpressionValue::Integer(v) => v.to_css(dest),
- QueryExpressionValue::Float(v) => v.to_css(dest),
- QueryExpressionValue::BoolInteger(v) => dest.write_str(if v { "1" } else { "0" }),
- QueryExpressionValue::NumberRatio(ratio) => ratio.to_css(dest),
- QueryExpressionValue::Resolution(ref r) => r.to_css(dest),
- QueryExpressionValue::Enumerated(value) => match for_expr.feature().evaluator {
- Evaluator::Enumerated { serializer, .. } => dest.write_str(&*serializer(value)),
- _ => unreachable!(),
- },
- }
- }
-
- fn parse<'i, 't>(
- for_feature: &QueryFeatureDescription,
- context: &ParserContext,
- input: &mut Parser<'i, 't>,
- ) -> Result<QueryExpressionValue, ParseError<'i>> {
- Ok(match for_feature.evaluator {
- Evaluator::OptionalLength(..) | Evaluator::Length(..) => {
- let length = Length::parse(context, input)?;
- QueryExpressionValue::Length(length)
- },
- Evaluator::Integer(..) => {
- let integer = Integer::parse(context, input)?;
- QueryExpressionValue::Integer(integer.value())
- },
- Evaluator::BoolInteger(..) => {
- let integer = Integer::parse_non_negative(context, input)?;
- let value = integer.value();
- if value > 1 {
- return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError));
- }
- QueryExpressionValue::BoolInteger(value == 1)
- },
- Evaluator::Float(..) => {
- let number = Number::parse(context, input)?;
- QueryExpressionValue::Float(number.get())
- },
- Evaluator::OptionalNumberRatio(..) | Evaluator::NumberRatio(..) => {
- use crate::values::specified::Ratio as SpecifiedRatio;
- let ratio = SpecifiedRatio::parse(context, input)?;
- QueryExpressionValue::NumberRatio(Ratio::new(ratio.0.get(), ratio.1.get()))
- },
- Evaluator::Resolution(..) => {
- QueryExpressionValue::Resolution(Resolution::parse(context, input)?)
- },
- Evaluator::Enumerated { parser, .. } => {
- QueryExpressionValue::Enumerated(parser(context, input)?)
- },
- })
- }
-}
diff --git a/components/style/queries/mod.rs b/components/style/queries/mod.rs
deleted file mode 100644
index ec11ab3721e..00000000000
--- a/components/style/queries/mod.rs
+++ /dev/null
@@ -1,19 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
-
-//! Code shared between [media queries][mq] and [container queries][cq].
-//!
-//! [mq]: https://drafts.csswg.org/mediaqueries/
-//! [cq]: https://drafts.csswg.org/css-contain-3/#container-rule
-
-pub mod condition;
-
-#[macro_use]
-pub mod feature;
-pub mod feature_expression;
-pub mod values;
-
-pub use self::condition::QueryCondition;
-pub use self::feature::FeatureFlags;
-pub use self::feature_expression::{FeatureType, QueryFeatureExpression};
diff --git a/components/style/queries/values.rs b/components/style/queries/values.rs
deleted file mode 100644
index f4934408c4d..00000000000
--- a/components/style/queries/values.rs
+++ /dev/null
@@ -1,36 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
-
-//! Common feature values between media and container features.
-
-use app_units::Au;
-use euclid::default::Size2D;
-
-/// The orientation media / container feature.
-/// https://drafts.csswg.org/mediaqueries-5/#orientation
-/// https://drafts.csswg.org/css-contain-3/#orientation
-#[derive(Clone, Copy, Debug, FromPrimitive, Parse, ToCss)]
-#[repr(u8)]
-#[allow(missing_docs)]
-pub enum Orientation {
- Portrait,
- Landscape,
-}
-
-impl Orientation {
- /// A helper to evaluate a orientation query given a generic size getter.
- pub fn eval(size: Size2D<Au>, value: Option<Self>) -> bool {
- let query_orientation = match value {
- Some(v) => v,
- None => return true,
- };
-
- // Per spec, square viewports should be 'portrait'
- let is_landscape = size.width > size.height;
- match query_orientation {
- Self::Landscape => is_landscape,
- Self::Portrait => !is_landscape,
- }
- }
-}
diff --git a/components/style/rule_cache.rs b/components/style/rule_cache.rs
deleted file mode 100644
index 604a92b724a..00000000000
--- a/components/style/rule_cache.rs
+++ /dev/null
@@ -1,187 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
-
-//! A cache from rule node to computed values, in order to cache reset
-//! properties.
-
-use crate::logical_geometry::WritingMode;
-use crate::properties::{ComputedValues, StyleBuilder};
-use crate::rule_tree::StrongRuleNode;
-use crate::selector_parser::PseudoElement;
-use crate::shared_lock::StylesheetGuards;
-use crate::values::computed::NonNegativeLength;
-use fxhash::FxHashMap;
-use servo_arc::Arc;
-use smallvec::SmallVec;
-
-/// The conditions for caching and matching a style in the rule cache.
-#[derive(Clone, Debug, Default)]
-pub struct RuleCacheConditions {
- uncacheable: bool,
- font_size: Option<NonNegativeLength>,
- writing_mode: Option<WritingMode>,
-}
-
-impl RuleCacheConditions {
- /// Sets the style as depending in the font-size value.
- pub fn set_font_size_dependency(&mut self, font_size: NonNegativeLength) {
- debug_assert!(self.font_size.map_or(true, |f| f == font_size));
- self.font_size = Some(font_size);
- }
-
- /// Sets the style as uncacheable.
- pub fn set_uncacheable(&mut self) {
- self.uncacheable = true;
- }
-
- /// Sets the style as depending in the writing-mode value `writing_mode`.
- pub fn set_writing_mode_dependency(&mut self, writing_mode: WritingMode) {
- debug_assert!(self.writing_mode.map_or(true, |wm| wm == writing_mode));
- self.writing_mode = Some(writing_mode);
- }
-
- /// Returns whether the current style's reset properties are cacheable.
- fn cacheable(&self) -> bool {
- !self.uncacheable
- }
-
- /// Returns whether `style` matches the conditions.
- fn matches(&self, style: &StyleBuilder) -> bool {
- if self.uncacheable {
- return false;
- }
-
- if let Some(fs) = self.font_size {
- if style.get_font().clone_font_size().computed_size != fs {
- return false;
- }
- }
-
- if let Some(wm) = self.writing_mode {
- if style.writing_mode != wm {
- return false;
- }
- }
-
- true
- }
-}
-
-/// A TLS cache from rules matched to computed values.
-pub struct RuleCache {
- // FIXME(emilio): Consider using LRUCache or something like that?
- map: FxHashMap<StrongRuleNode, SmallVec<[(RuleCacheConditions, Arc<ComputedValues>); 1]>>,
-}
-
-impl RuleCache {
- /// Creates an empty `RuleCache`.
- pub fn new() -> Self {
- Self {
- map: FxHashMap::default(),
- }
- }
-
- /// Walk the rule tree and return a rule node for using as the key
- /// for rule cache.
- ///
- /// It currently skips a rule node when it is neither from a style
- /// rule, nor containing any declaration of reset property. We don't
- /// skip style rule so that we don't need to walk a long way in the
- /// worst case. Skipping declarations rule nodes should be enough
- /// to address common cases that rule cache would fail to share
- /// when using the rule node directly, like preshint, style attrs,
- /// and animations.
- fn get_rule_node_for_cache<'r>(
- guards: &StylesheetGuards,
- mut rule_node: Option<&'r StrongRuleNode>,
- ) -> Option<&'r StrongRuleNode> {
- while let Some(node) = rule_node {
- match node.style_source() {
- Some(s) => match s.as_declarations() {
- Some(decls) => {
- let cascade_level = node.cascade_level();
- let decls = decls.read_with(cascade_level.guard(guards));
- if decls.contains_any_reset() {
- break;
- }
- },
- None => break,
- },
- None => {},
- }
- rule_node = node.parent();
- }
- rule_node
- }
-
- /// Finds a node in the properties matched cache.
- ///
- /// This needs to receive a `StyleBuilder` with the `early` properties
- /// already applied.
- pub fn find(
- &self,
- guards: &StylesheetGuards,
- builder_with_early_props: &StyleBuilder,
- ) -> Option<&ComputedValues> {
- // A pseudo-element with property restrictions can result in different
- // computed values if it's also used for a non-pseudo.
- if builder_with_early_props
- .pseudo
- .and_then(|p| p.property_restriction())
- .is_some()
- {
- return None;
- }
-
- let rules = builder_with_early_props.rules.as_ref();
- let rules = Self::get_rule_node_for_cache(guards, rules)?;
- let cached_values = self.map.get(rules)?;
-
- for &(ref conditions, ref values) in cached_values.iter() {
- if conditions.matches(builder_with_early_props) {
- debug!("Using cached reset style with conditions {:?}", conditions);
- return Some(&**values);
- }
- }
- None
- }
-
- /// Inserts a node into the rules cache if possible.
- ///
- /// Returns whether the style was inserted into the cache.
- pub fn insert_if_possible(
- &mut self,
- guards: &StylesheetGuards,
- style: &Arc<ComputedValues>,
- pseudo: Option<&PseudoElement>,
- conditions: &RuleCacheConditions,
- ) -> bool {
- if !conditions.cacheable() {
- return false;
- }
-
- // A pseudo-element with property restrictions can result in different
- // computed values if it's also used for a non-pseudo.
- if pseudo.and_then(|p| p.property_restriction()).is_some() {
- return false;
- }
-
- let rules = style.rules.as_ref();
- let rules = match Self::get_rule_node_for_cache(guards, rules) {
- Some(r) => r.clone(),
- None => return false,
- };
-
- debug!(
- "Inserting cached reset style with conditions {:?}",
- conditions
- );
- self.map
- .entry(rules)
- .or_insert_with(SmallVec::new)
- .push((conditions.clone(), style.clone()));
-
- true
- }
-}
diff --git a/components/style/rule_collector.rs b/components/style/rule_collector.rs
deleted file mode 100644
index 058d6823179..00000000000
--- a/components/style/rule_collector.rs
+++ /dev/null
@@ -1,505 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
-
-//! Collects a series of applicable rules for a given element.
-
-use crate::applicable_declarations::{ApplicableDeclarationBlock, ApplicableDeclarationList};
-use crate::dom::{TElement, TNode, TShadowRoot};
-use crate::properties::{AnimationDeclarations, PropertyDeclarationBlock};
-use crate::rule_tree::{CascadeLevel, ShadowCascadeOrder};
-use crate::selector_map::SelectorMap;
-use crate::selector_parser::PseudoElement;
-use crate::shared_lock::Locked;
-use crate::stylesheets::{layer_rule::LayerOrder, Origin};
-use crate::stylist::{AuthorStylesEnabled, CascadeData, Rule, RuleInclusion, Stylist};
-use selectors::matching::{MatchingContext, MatchingMode};
-use servo_arc::ArcBorrow;
-use smallvec::SmallVec;
-
-/// This is a bit of a hack so <svg:use> matches the rules of the enclosing
-/// tree.
-///
-/// This function returns the containing shadow host ignoring <svg:use> shadow
-/// trees, since those match the enclosing tree's rules.
-///
-/// Only a handful of places need to really care about this. This is not a
-/// problem for invalidation and that kind of stuff because they still don't
-/// match rules based on elements outside of the shadow tree, and because the
-/// <svg:use> subtrees are immutable and recreated each time the source tree
-/// changes.
-///
-/// We historically allow cross-document <svg:use> to have these rules applied,
-/// but I think that's not great. Gecko is the only engine supporting that.
-///
-/// See https://github.com/w3c/svgwg/issues/504 for the relevant spec
-/// discussion.
-#[inline]
-pub fn containing_shadow_ignoring_svg_use<E: TElement>(
- element: E,
-) -> Option<<E::ConcreteNode as TNode>::ConcreteShadowRoot> {
- let mut shadow = element.containing_shadow()?;
- loop {
- let host = shadow.host();
- let host_is_svg_use_element =
- host.is_svg_element() && host.local_name() == &**local_name!("use");
- if !host_is_svg_use_element {
- return Some(shadow);
- }
- debug_assert!(
- shadow.style_data().is_none(),
- "We allow no stylesheets in <svg:use> subtrees"
- );
- shadow = host.containing_shadow()?;
- }
-}
-
-/// An object that we use with all the intermediate state needed for the
-/// cascade.
-///
-/// This is done basically to be able to organize the cascade in smaller
-/// functions, and be able to reason about it easily.
-pub struct RuleCollector<'a, 'b: 'a, E>
-where
- E: TElement,
-{
- element: E,
- rule_hash_target: E,
- stylist: &'a Stylist,
- pseudo_element: Option<&'a PseudoElement>,
- style_attribute: Option<ArcBorrow<'a, Locked<PropertyDeclarationBlock>>>,
- smil_override: Option<ArcBorrow<'a, Locked<PropertyDeclarationBlock>>>,
- animation_declarations: AnimationDeclarations,
- rule_inclusion: RuleInclusion,
- rules: &'a mut ApplicableDeclarationList,
- context: &'a mut MatchingContext<'b, E::Impl>,
- matches_user_and_content_rules: bool,
- matches_document_author_rules: bool,
- in_sort_scope: bool,
-}
-
-impl<'a, 'b: 'a, E> RuleCollector<'a, 'b, E>
-where
- E: TElement,
-{
- /// Trivially construct a new collector.
- pub fn new(
- stylist: &'a Stylist,
- element: E,
- pseudo_element: Option<&'a PseudoElement>,
- style_attribute: Option<ArcBorrow<'a, Locked<PropertyDeclarationBlock>>>,
- smil_override: Option<ArcBorrow<'a, Locked<PropertyDeclarationBlock>>>,
- animation_declarations: AnimationDeclarations,
- rule_inclusion: RuleInclusion,
- rules: &'a mut ApplicableDeclarationList,
- context: &'a mut MatchingContext<'b, E::Impl>,
- ) -> Self {
- // When we're matching with matching_mode =
- // `ForStatelessPseudoeElement`, the "target" for the rule hash is the
- // element itself, since it's what's generating the pseudo-element.
- let rule_hash_target = match context.matching_mode() {
- MatchingMode::ForStatelessPseudoElement => element,
- MatchingMode::Normal => element.rule_hash_target(),
- };
-
- let matches_user_and_content_rules = rule_hash_target.matches_user_and_content_rules();
-
- // Gecko definitely has pseudo-elements with style attributes, like
- // ::-moz-color-swatch.
- debug_assert!(
- cfg!(feature = "gecko") || style_attribute.is_none() || pseudo_element.is_none(),
- "Style attributes do not apply to pseudo-elements"
- );
- debug_assert!(pseudo_element.map_or(true, |p| !p.is_precomputed()));
-
- Self {
- element,
- rule_hash_target,
- stylist,
- pseudo_element,
- style_attribute,
- smil_override,
- animation_declarations,
- rule_inclusion,
- context,
- rules,
- matches_user_and_content_rules,
- matches_document_author_rules: matches_user_and_content_rules,
- in_sort_scope: false,
- }
- }
-
- /// Sets up the state necessary to collect rules from a given DOM tree
- /// (either the document tree, or a shadow tree).
- ///
- /// All rules in the same tree need to be matched together, and this
- /// function takes care of sorting them by specificity and source order.
- #[inline]
- fn in_tree(&mut self, host: Option<E>, f: impl FnOnce(&mut Self)) {
- debug_assert!(!self.in_sort_scope, "Nested sorting makes no sense");
- let start = self.rules.len();
- self.in_sort_scope = true;
- let old_host = self.context.current_host.take();
- self.context.current_host = host.map(|e| e.opaque());
- f(self);
- if start != self.rules.len() {
- self.rules[start..].sort_unstable_by_key(|block| {
- (block.layer_order(), block.specificity, block.source_order())
- });
- }
- self.context.current_host = old_host;
- self.in_sort_scope = false;
- }
-
- #[inline]
- fn in_shadow_tree(&mut self, host: E, f: impl FnOnce(&mut Self)) {
- self.in_tree(Some(host), f);
- }
-
- fn collect_stylist_rules(&mut self, origin: Origin) {
- let cascade_level = match origin {
- Origin::UserAgent => CascadeLevel::UANormal,
- Origin::User => CascadeLevel::UserNormal,
- Origin::Author => CascadeLevel::same_tree_author_normal(),
- };
-
- let cascade_data = self.stylist.cascade_data().borrow_for_origin(origin);
- let map = match cascade_data.normal_rules(self.pseudo_element) {
- Some(m) => m,
- None => return,
- };
-
- self.in_tree(None, |collector| {
- collector.collect_rules_in_map(map, cascade_level, cascade_data);
- });
- }
-
- fn collect_user_agent_rules(&mut self) {
- self.collect_stylist_rules(Origin::UserAgent);
- }
-
- fn collect_user_rules(&mut self) {
- if !self.matches_user_and_content_rules {
- return;
- }
-
- self.collect_stylist_rules(Origin::User);
- }
-
- /// Presentational hints.
- ///
- /// These go before author rules, but after user rules, see:
- /// https://drafts.csswg.org/css-cascade/#preshint
- fn collect_presentational_hints(&mut self) {
- if self.pseudo_element.is_some() {
- return;
- }
-
- let length_before_preshints = self.rules.len();
- self.element
- .synthesize_presentational_hints_for_legacy_attributes(
- self.context.visited_handling(),
- self.rules,
- );
- if cfg!(debug_assertions) {
- if self.rules.len() != length_before_preshints {
- for declaration in &self.rules[length_before_preshints..] {
- assert_eq!(declaration.level(), CascadeLevel::PresHints);
- }
- }
- }
- }
-
- #[inline]
- fn collect_rules_in_list(
- &mut self,
- part_rules: &[Rule],
- cascade_level: CascadeLevel,
- cascade_data: &CascadeData,
- ) {
- debug_assert!(self.in_sort_scope, "Rules gotta be sorted");
- SelectorMap::get_matching_rules(
- self.element,
- part_rules,
- &mut self.rules,
- &mut self.context,
- cascade_level,
- cascade_data,
- &self.stylist,
- );
- }
-
- #[inline]
- fn collect_rules_in_map(
- &mut self,
- map: &SelectorMap<Rule>,
- cascade_level: CascadeLevel,
- cascade_data: &CascadeData,
- ) {
- debug_assert!(self.in_sort_scope, "Rules gotta be sorted");
- map.get_all_matching_rules(
- self.element,
- self.rule_hash_target,
- &mut self.rules,
- &mut self.context,
- cascade_level,
- cascade_data,
- &self.stylist,
- );
- }
-
- /// Collects the rules for the ::slotted pseudo-element and the :host
- /// pseudo-class.
- fn collect_host_and_slotted_rules(&mut self) {
- let mut slots = SmallVec::<[_; 3]>::new();
- let mut current = self.rule_hash_target.assigned_slot();
- let mut shadow_cascade_order = ShadowCascadeOrder::for_outermost_shadow_tree();
-
- while let Some(slot) = current {
- debug_assert!(
- self.matches_user_and_content_rules,
- "We should not slot NAC anywhere"
- );
- slots.push(slot);
- current = slot.assigned_slot();
- shadow_cascade_order.dec();
- }
-
- self.collect_host_rules(shadow_cascade_order);
-
- // Match slotted rules in reverse order, so that the outer slotted rules
- // come before the inner rules (and thus have less priority).
- for slot in slots.iter().rev() {
- shadow_cascade_order.inc();
-
- let shadow = slot.containing_shadow().unwrap();
- let data = match shadow.style_data() {
- Some(d) => d,
- None => continue,
- };
- let slotted_rules = match data.slotted_rules(self.pseudo_element) {
- Some(r) => r,
- None => continue,
- };
-
- self.in_shadow_tree(shadow.host(), |collector| {
- let cascade_level = CascadeLevel::AuthorNormal {
- shadow_cascade_order,
- };
- collector.collect_rules_in_map(slotted_rules, cascade_level, data);
- });
- }
- }
-
- fn collect_rules_from_containing_shadow_tree(&mut self) {
- if !self.matches_user_and_content_rules {
- return;
- }
-
- let containing_shadow = containing_shadow_ignoring_svg_use(self.rule_hash_target);
- let containing_shadow = match containing_shadow {
- Some(s) => s,
- None => return,
- };
-
- self.matches_document_author_rules = false;
-
- let cascade_data = match containing_shadow.style_data() {
- Some(c) => c,
- None => return,
- };
-
- let cascade_level = CascadeLevel::same_tree_author_normal();
- self.in_shadow_tree(containing_shadow.host(), |collector| {
- if let Some(map) = cascade_data.normal_rules(collector.pseudo_element) {
- collector.collect_rules_in_map(map, cascade_level, cascade_data);
- }
-
- // Collect rules from :host::part() and such
- let hash_target = collector.rule_hash_target;
- if !hash_target.has_part_attr() {
- return;
- }
-
- let part_rules = match cascade_data.part_rules(collector.pseudo_element) {
- Some(p) => p,
- None => return,
- };
-
- hash_target.each_part(|part| {
- if let Some(part_rules) = part_rules.get(&part.0) {
- collector.collect_rules_in_list(part_rules, cascade_level, cascade_data);
- }
- });
- });
- }
-
- /// Collects the rules for the :host pseudo-class.
- fn collect_host_rules(&mut self, shadow_cascade_order: ShadowCascadeOrder) {
- let shadow = match self.rule_hash_target.shadow_root() {
- Some(s) => s,
- None => return,
- };
-
- let style_data = match shadow.style_data() {
- Some(d) => d,
- None => return,
- };
-
- let host_rules = match style_data.host_rules(self.pseudo_element) {
- Some(rules) => rules,
- None => return,
- };
-
- let rule_hash_target = self.rule_hash_target;
- self.in_shadow_tree(rule_hash_target, |collector| {
- let cascade_level = CascadeLevel::AuthorNormal {
- shadow_cascade_order,
- };
- collector.collect_rules_in_map(host_rules, cascade_level, style_data);
- });
- }
-
- fn collect_document_author_rules(&mut self) {
- if !self.matches_document_author_rules {
- return;
- }
-
- self.collect_stylist_rules(Origin::Author);
- }
-
- fn collect_part_rules_from_outer_trees(&mut self) {
- if !self.rule_hash_target.has_part_attr() {
- return;
- }
-
- let mut inner_shadow = match self.rule_hash_target.containing_shadow() {
- Some(s) => s,
- None => return,
- };
-
- let mut shadow_cascade_order = ShadowCascadeOrder::for_innermost_containing_tree();
-
- let mut parts = SmallVec::<[_; 3]>::new();
- self.rule_hash_target.each_part(|p| parts.push(p.clone()));
-
- loop {
- if parts.is_empty() {
- return;
- }
-
- let inner_shadow_host = inner_shadow.host();
- let outer_shadow = inner_shadow_host.containing_shadow();
- let cascade_data = match outer_shadow {
- Some(shadow) => shadow.style_data(),
- None => Some(
- self.stylist
- .cascade_data()
- .borrow_for_origin(Origin::Author),
- ),
- };
-
- if let Some(cascade_data) = cascade_data {
- if let Some(part_rules) = cascade_data.part_rules(self.pseudo_element) {
- let containing_host = outer_shadow.map(|s| s.host());
- let cascade_level = CascadeLevel::AuthorNormal {
- shadow_cascade_order,
- };
- self.in_tree(containing_host, |collector| {
- for p in &parts {
- if let Some(part_rules) = part_rules.get(&p.0) {
- collector.collect_rules_in_list(
- part_rules,
- cascade_level,
- cascade_data,
- );
- }
- }
- });
- shadow_cascade_order.inc();
- }
- }
-
- inner_shadow = match outer_shadow {
- Some(s) => s,
- None => break, // Nowhere to export to.
- };
-
- let mut new_parts = SmallVec::new();
- for part in &parts {
- inner_shadow_host.each_exported_part(part, |exported_part| {
- new_parts.push(exported_part.clone());
- });
- }
- parts = new_parts;
- }
- }
-
- fn collect_style_attribute(&mut self) {
- if let Some(sa) = self.style_attribute {
- self.rules
- .push(ApplicableDeclarationBlock::from_declarations(
- sa.clone_arc(),
- CascadeLevel::same_tree_author_normal(),
- LayerOrder::style_attribute(),
- ));
- }
- }
-
- fn collect_animation_rules(&mut self) {
- if let Some(so) = self.smil_override {
- self.rules
- .push(ApplicableDeclarationBlock::from_declarations(
- so.clone_arc(),
- CascadeLevel::SMILOverride,
- LayerOrder::root(),
- ));
- }
-
- // The animations sheet (CSS animations, script-generated
- // animations, and CSS transitions that are no longer tied to CSS
- // markup).
- if let Some(anim) = self.animation_declarations.animations.take() {
- self.rules
- .push(ApplicableDeclarationBlock::from_declarations(
- anim,
- CascadeLevel::Animations,
- LayerOrder::root(),
- ));
- }
-
- // The transitions sheet (CSS transitions that are tied to CSS
- // markup).
- if let Some(anim) = self.animation_declarations.transitions.take() {
- self.rules
- .push(ApplicableDeclarationBlock::from_declarations(
- anim,
- CascadeLevel::Transitions,
- LayerOrder::root(),
- ));
- }
- }
-
- /// Collects all the rules, leaving the result in `self.rules`.
- ///
- /// Note that `!important` rules are handled during rule tree insertion.
- pub fn collect_all(mut self) {
- self.collect_user_agent_rules();
- self.collect_user_rules();
- if self.rule_inclusion == RuleInclusion::DefaultOnly {
- return;
- }
- self.collect_presentational_hints();
- // FIXME(emilio): Should the author styles enabled stuff avoid the
- // presentational hints from getting pushed? See bug 1505770.
- if self.stylist.author_styles_enabled() == AuthorStylesEnabled::No {
- return;
- }
- self.collect_host_and_slotted_rules();
- self.collect_rules_from_containing_shadow_tree();
- self.collect_document_author_rules();
- self.collect_style_attribute();
- self.collect_part_rules_from_outer_trees();
- self.collect_animation_rules();
- }
-}
diff --git a/components/style/rule_tree/core.rs b/components/style/rule_tree/core.rs
deleted file mode 100644
index 85584a0e224..00000000000
--- a/components/style/rule_tree/core.rs
+++ /dev/null
@@ -1,772 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
-
-#![allow(unsafe_code)]
-
-use crate::applicable_declarations::CascadePriority;
-use crate::shared_lock::StylesheetGuards;
-use crate::stylesheets::layer_rule::LayerOrder;
-use malloc_size_of::{MallocShallowSizeOf, MallocSizeOf, MallocSizeOfOps};
-use parking_lot::RwLock;
-use smallvec::SmallVec;
-use std::fmt;
-use std::hash;
-use std::io::Write;
-use std::mem;
-use std::ptr;
-use std::sync::atomic::{self, AtomicPtr, AtomicUsize, Ordering};
-
-use super::map::{Entry, Map};
-use super::unsafe_box::UnsafeBox;
-use super::{CascadeLevel, StyleSource};
-
-/// The rule tree, the structure servo uses to preserve the results of selector
-/// matching.
-///
-/// This is organized as a tree of rules. When a node matches a set of rules,
-/// they're inserted in order in the tree, starting with the less specific one.
-///
-/// When a rule is inserted in the tree, other elements may share the path up to
-/// a given rule. If that's the case, we don't duplicate child nodes, but share
-/// them.
-///
-/// When the rule node refcount drops to zero, it doesn't get freed. It gets
-/// instead put into a free list, and it is potentially GC'd after a while.
-///
-/// That way, a rule node that represents a likely-to-match-again rule (like a
-/// :hover rule) can be reused if we haven't GC'd it yet.
-#[derive(Debug)]
-pub struct RuleTree {
- root: StrongRuleNode,
-}
-
-impl Drop for RuleTree {
- fn drop(&mut self) {
- unsafe { self.swap_free_list_and_gc(ptr::null_mut()) }
- }
-}
-
-impl MallocSizeOf for RuleTree {
- fn size_of(&self, ops: &mut MallocSizeOfOps) -> usize {
- let mut n = 0;
- let mut stack = SmallVec::<[_; 32]>::new();
- stack.push(self.root.clone());
-
- while let Some(node) = stack.pop() {
- n += unsafe { ops.malloc_size_of(&*node.p) };
- let children = node.p.children.read();
- children.shallow_size_of(ops);
- for c in &*children {
- stack.push(unsafe { c.upgrade() });
- }
- }
-
- n
- }
-}
-
-#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
-struct ChildKey(CascadePriority, ptr::NonNull<()>);
-unsafe impl Send for ChildKey {}
-unsafe impl Sync for ChildKey {}
-
-impl RuleTree {
- /// Construct a new rule tree.
- pub fn new() -> Self {
- RuleTree {
- root: StrongRuleNode::new(Box::new(RuleNode::root())),
- }
- }
-
- /// Get the root rule node.
- pub fn root(&self) -> &StrongRuleNode {
- &self.root
- }
-
- /// This can only be called when no other threads is accessing this tree.
- pub fn gc(&self) {
- unsafe { self.swap_free_list_and_gc(RuleNode::DANGLING_PTR) }
- }
-
- /// This can only be called when no other threads is accessing this tree.
- pub fn maybe_gc(&self) {
- #[cfg(debug_assertions)]
- self.maybe_dump_stats();
-
- if self.root.p.approximate_free_count.load(Ordering::Relaxed) > RULE_TREE_GC_INTERVAL {
- self.gc();
- }
- }
-
- #[cfg(debug_assertions)]
- fn maybe_dump_stats(&self) {
- use itertools::Itertools;
- use std::cell::Cell;
- use std::time::{Duration, Instant};
-
- if !log_enabled!(log::Level::Trace) {
- return;
- }
-
- const RULE_TREE_STATS_INTERVAL: Duration = Duration::from_secs(2);
-
- thread_local! {
- pub static LAST_STATS: Cell<Instant> = Cell::new(Instant::now());
- };
-
- let should_dump = LAST_STATS.with(|s| {
- let now = Instant::now();
- if now.duration_since(s.get()) < RULE_TREE_STATS_INTERVAL {
- return false;
- }
- s.set(now);
- true
- });
-
- if !should_dump {
- return;
- }
-
- let mut children_count = fxhash::FxHashMap::default();
-
- let mut stack = SmallVec::<[_; 32]>::new();
- stack.push(self.root.clone());
- while let Some(node) = stack.pop() {
- let children = node.p.children.read();
- *children_count.entry(children.len()).or_insert(0) += 1;
- for c in &*children {
- stack.push(unsafe { c.upgrade() });
- }
- }
-
- trace!("Rule tree stats:");
- let counts = children_count.keys().sorted();
- for count in counts {
- trace!(" {} - {}", count, children_count[count]);
- }
- }
-
- /// Steals the free list and drops its contents.
- unsafe fn swap_free_list_and_gc(&self, ptr: *mut RuleNode) {
- let root = &self.root.p;
-
- debug_assert!(!root.next_free.load(Ordering::Relaxed).is_null());
-
- // Reset the approximate free count to zero, as we are going to steal
- // the free list.
- root.approximate_free_count.store(0, Ordering::Relaxed);
-
- // Steal the free list head. Memory loads on nodes while iterating it
- // must observe any prior changes that occured so this requires
- // acquire ordering, but there are no writes that need to be kept
- // before this swap so there is no need for release.
- let mut head = root.next_free.swap(ptr, Ordering::Acquire);
-
- while head != RuleNode::DANGLING_PTR {
- debug_assert!(!head.is_null());
-
- let mut node = UnsafeBox::from_raw(head);
-
- // The root node cannot go on the free list.
- debug_assert!(node.root.is_some());
-
- // The refcount of nodes on the free list never goes below 1.
- debug_assert!(node.refcount.load(Ordering::Relaxed) > 0);
-
- // No one else is currently writing to that field. Get the address
- // of the next node in the free list and replace it with null,
- // other threads will now consider that this node is not on the
- // free list.
- head = node.next_free.swap(ptr::null_mut(), Ordering::Relaxed);
-
- // This release write synchronises with the acquire fence in
- // `WeakRuleNode::upgrade`, making sure that if `upgrade` observes
- // decrements the refcount to 0, it will also observe the
- // `node.next_free` swap to null above.
- if node.refcount.fetch_sub(1, Ordering::Release) == 1 {
- // And given it observed the null swap above, it will need
- // `pretend_to_be_on_free_list` to finish its job, writing
- // `RuleNode::DANGLING_PTR` in `node.next_free`.
- RuleNode::pretend_to_be_on_free_list(&node);
-
- // Drop this node now that we just observed its refcount going
- // down to zero.
- RuleNode::drop_without_free_list(&mut node);
- }
- }
- }
-}
-
-/// The number of RuleNodes added to the free list before we will consider
-/// doing a GC when calling maybe_gc(). (The value is copied from Gecko,
-/// where it likely did not result from a rigorous performance analysis.)
-const RULE_TREE_GC_INTERVAL: usize = 300;
-
-/// A node in the rule tree.
-struct RuleNode {
- /// The root node. Only the root has no root pointer, for obvious reasons.
- root: Option<WeakRuleNode>,
-
- /// The parent rule node. Only the root has no parent.
- parent: Option<StrongRuleNode>,
-
- /// The actual style source, either coming from a selector in a StyleRule,
- /// or a raw property declaration block (like the style attribute).
- ///
- /// None for the root node.
- source: Option<StyleSource>,
-
- /// The cascade level + layer order this rule is positioned at.
- cascade_priority: CascadePriority,
-
- /// The refcount of this node.
- ///
- /// Starts at one. Incremented in `StrongRuleNode::clone` and
- /// `WeakRuleNode::upgrade`. Decremented in `StrongRuleNode::drop`
- /// and `RuleTree::swap_free_list_and_gc`.
- ///
- /// If a non-root node's refcount reaches zero, it is incremented back to at
- /// least one in `RuleNode::pretend_to_be_on_free_list` until the caller who
- /// observed it dropping to zero had a chance to try to remove it from its
- /// parent's children list.
- ///
- /// The refcount should never be decremented to zero if the value in
- /// `next_free` is not null.
- refcount: AtomicUsize,
-
- /// Only used for the root, stores the number of free rule nodes that are
- /// around.
- approximate_free_count: AtomicUsize,
-
- /// The children of a given rule node. Children remove themselves from here
- /// when they go away.
- children: RwLock<Map<ChildKey, WeakRuleNode>>,
-
- /// This field has two different meanings depending on whether this is the
- /// root node or not.
- ///
- /// If it is the root, it represents the head of the free list. It may be
- /// null, which means the free list is gone because the tree was dropped,
- /// and it may be `RuleNode::DANGLING_PTR`, which means the free list is
- /// empty.
- ///
- /// If it is not the root node, this field is either null if the node is
- /// not on the free list, `RuleNode::DANGLING_PTR` if it is the last item
- /// on the free list or the node is pretending to be on the free list, or
- /// any valid non-null pointer representing the next item on the free list
- /// after this one.
- ///
- /// See `RuleNode::push_on_free_list`, `swap_free_list_and_gc`, and
- /// `WeakRuleNode::upgrade`.
- ///
- /// Two threads should never attempt to put the same node on the free list
- /// both at the same time.
- next_free: AtomicPtr<RuleNode>,
-}
-
-// On Gecko builds, hook into the leak checking machinery.
-#[cfg(feature = "gecko_refcount_logging")]
-mod gecko_leak_checking {
- use super::RuleNode;
- use std::mem::size_of;
- use std::os::raw::{c_char, c_void};
-
- extern "C" {
- fn NS_LogCtor(aPtr: *mut c_void, aTypeName: *const c_char, aSize: u32);
- fn NS_LogDtor(aPtr: *mut c_void, aTypeName: *const c_char, aSize: u32);
- }
- static NAME: &'static [u8] = b"RuleNode\0";
-
- /// Logs the creation of a heap-allocated object to Gecko's leak-checking machinery.
- pub(super) fn log_ctor(ptr: *const RuleNode) {
- let s = NAME as *const [u8] as *const u8 as *const c_char;
- unsafe {
- NS_LogCtor(ptr as *mut c_void, s, size_of::<RuleNode>() as u32);
- }
- }
-
- /// Logs the destruction of a heap-allocated object to Gecko's leak-checking machinery.
- pub(super) fn log_dtor(ptr: *const RuleNode) {
- let s = NAME as *const [u8] as *const u8 as *const c_char;
- unsafe {
- NS_LogDtor(ptr as *mut c_void, s, size_of::<RuleNode>() as u32);
- }
- }
-}
-
-#[inline(always)]
-fn log_new(_ptr: *const RuleNode) {
- #[cfg(feature = "gecko_refcount_logging")]
- gecko_leak_checking::log_ctor(_ptr);
-}
-
-#[inline(always)]
-fn log_drop(_ptr: *const RuleNode) {
- #[cfg(feature = "gecko_refcount_logging")]
- gecko_leak_checking::log_dtor(_ptr);
-}
-
-impl RuleNode {
- const DANGLING_PTR: *mut Self = ptr::NonNull::dangling().as_ptr();
-
- unsafe fn new(
- root: WeakRuleNode,
- parent: StrongRuleNode,
- source: StyleSource,
- cascade_priority: CascadePriority,
- ) -> Self {
- debug_assert!(root.p.parent.is_none());
- RuleNode {
- root: Some(root),
- parent: Some(parent),
- source: Some(source),
- cascade_priority,
- refcount: AtomicUsize::new(1),
- children: Default::default(),
- approximate_free_count: AtomicUsize::new(0),
- next_free: AtomicPtr::new(ptr::null_mut()),
- }
- }
-
- fn root() -> Self {
- RuleNode {
- root: None,
- parent: None,
- source: None,
- cascade_priority: CascadePriority::new(CascadeLevel::UANormal, LayerOrder::root()),
- refcount: AtomicUsize::new(1),
- approximate_free_count: AtomicUsize::new(0),
- children: Default::default(),
- next_free: AtomicPtr::new(RuleNode::DANGLING_PTR),
- }
- }
-
- fn key(&self) -> ChildKey {
- ChildKey(
- self.cascade_priority,
- self.source
- .as_ref()
- .expect("Called key() on the root node")
- .key(),
- )
- }
-
- /// Drops a node without ever putting it on the free list.
- ///
- /// Note that the node may not be dropped if we observe that its refcount
- /// isn't zero anymore when we write-lock its parent's children map to
- /// remove it.
- ///
- /// This loops over parents of dropped nodes if their own refcount reaches
- /// zero to avoid recursion when dropping deep hierarchies of nodes.
- ///
- /// For non-root nodes, this should always be preceded by a call of
- /// `RuleNode::pretend_to_be_on_free_list`.
- unsafe fn drop_without_free_list(this: &mut UnsafeBox<Self>) {
- // We clone the box and shadow the original one to be able to loop
- // over its ancestors if they also need to be dropped.
- let mut this = UnsafeBox::clone(this);
- loop {
- // If the node has a parent, we need to remove it from its parent's
- // children list.
- if let Some(parent) = this.parent.as_ref() {
- debug_assert!(!this.next_free.load(Ordering::Relaxed).is_null());
-
- // We lock the parent's children list, which means no other
- // thread will have any more opportunity to resurrect the node
- // anymore.
- let mut children = parent.p.children.write();
-
- this.next_free.store(ptr::null_mut(), Ordering::Relaxed);
-
- // We decrement the counter to remove the "pretend to be
- // on the free list" reference.
- let old_refcount = this.refcount.fetch_sub(1, Ordering::Release);
- debug_assert!(old_refcount != 0);
- if old_refcount != 1 {
- // Other threads resurrected this node and those references
- // are still alive, we have nothing to do anymore.
- return;
- }
-
- // We finally remove the node from its parent's children list,
- // there are now no other references to it and it cannot
- // be resurrected anymore even after we unlock the list.
- debug!(
- "Remove from child list: {:?}, parent: {:?}",
- this.as_mut_ptr(),
- this.parent.as_ref().map(|p| p.p.as_mut_ptr())
- );
- let weak = children.remove(&this.key(), |node| node.p.key()).unwrap();
- assert_eq!(weak.p.as_mut_ptr(), this.as_mut_ptr());
- } else {
- debug_assert_eq!(this.next_free.load(Ordering::Relaxed), ptr::null_mut());
- debug_assert_eq!(this.refcount.load(Ordering::Relaxed), 0);
- }
-
- // We are going to drop this node for good this time, as per the
- // usual refcounting protocol we need an acquire fence here before
- // we run the destructor.
- //
- // See https://github.com/rust-lang/rust/pull/41714#issuecomment-298996916
- // for why it doesn't matter whether this is a load or a fence.
- atomic::fence(Ordering::Acquire);
-
- // Remove the parent reference from the child to avoid
- // recursively dropping it and putting it on the free list.
- let parent = UnsafeBox::deref_mut(&mut this).parent.take();
-
- // We now drop the actual box and its contents, no one should
- // access the current value in `this` anymore.
- log_drop(&*this);
- UnsafeBox::drop(&mut this);
-
- if let Some(parent) = parent {
- // We will attempt to drop the node's parent without the free
- // list, so we clone the inner unsafe box and forget the
- // original parent to avoid running its `StrongRuleNode`
- // destructor which would attempt to use the free list if it
- // still exists.
- this = UnsafeBox::clone(&parent.p);
- mem::forget(parent);
- if this.refcount.fetch_sub(1, Ordering::Release) == 1 {
- debug_assert_eq!(this.next_free.load(Ordering::Relaxed), ptr::null_mut());
- if this.root.is_some() {
- RuleNode::pretend_to_be_on_free_list(&this);
- }
- // Parent also reached refcount zero, we loop to drop it.
- continue;
- }
- }
-
- return;
- }
- }
-
- /// Pushes this node on the tree's free list. Returns false if the free list
- /// is gone. Should only be called after we decremented a node's refcount
- /// to zero and pretended to be on the free list.
- unsafe fn push_on_free_list(this: &UnsafeBox<Self>) -> bool {
- let root = &this.root.as_ref().unwrap().p;
-
- debug_assert!(this.refcount.load(Ordering::Relaxed) > 0);
- debug_assert_eq!(this.next_free.load(Ordering::Relaxed), Self::DANGLING_PTR);
-
- // Increment the approximate free count by one.
- root.approximate_free_count.fetch_add(1, Ordering::Relaxed);
-
- // If the compare-exchange operation fails in the loop, we will retry
- // with the new head value, so this can be a relaxed load.
- let mut head = root.next_free.load(Ordering::Relaxed);
-
- while !head.is_null() {
- // Two threads can never attempt to push the same node on the free
- // list both at the same time, so whoever else pushed a node on the
- // free list cannot have done so with this node.
- debug_assert_ne!(head, this.as_mut_ptr());
-
- // Store the current head of the free list in this node.
- this.next_free.store(head, Ordering::Relaxed);
-
- // Any thread acquiring the free list must observe the previous
- // next_free changes that occured, hence the release ordering
- // on success.
- match root.next_free.compare_exchange_weak(
- head,
- this.as_mut_ptr(),
- Ordering::Release,
- Ordering::Relaxed,
- ) {
- Ok(_) => {
- // This node is now on the free list, caller should not use
- // the node anymore.
- return true;
- },
- Err(new_head) => head = new_head,
- }
- }
-
- // Tree was dropped and free list has been destroyed. We did not push
- // this node on the free list but we still pretend to be on the free
- // list to be ready to call `drop_without_free_list`.
- false
- }
-
- /// Makes the node pretend to be on the free list. This will increment the
- /// refcount by 1 and store `Self::DANGLING_PTR` in `next_free`. This
- /// method should only be called after caller decremented the refcount to
- /// zero, with the null pointer stored in `next_free`.
- unsafe fn pretend_to_be_on_free_list(this: &UnsafeBox<Self>) {
- debug_assert_eq!(this.next_free.load(Ordering::Relaxed), ptr::null_mut());
- this.refcount.fetch_add(1, Ordering::Relaxed);
- this.next_free.store(Self::DANGLING_PTR, Ordering::Release);
- }
-
- fn as_mut_ptr(&self) -> *mut RuleNode {
- self as *const RuleNode as *mut RuleNode
- }
-}
-
-pub(crate) struct WeakRuleNode {
- p: UnsafeBox<RuleNode>,
-}
-
-/// A strong reference to a rule node.
-pub struct StrongRuleNode {
- p: UnsafeBox<RuleNode>,
-}
-
-#[cfg(feature = "servo")]
-malloc_size_of_is_0!(StrongRuleNode);
-
-impl StrongRuleNode {
- fn new(n: Box<RuleNode>) -> Self {
- debug_assert_eq!(n.parent.is_none(), !n.source.is_some());
-
- log_new(&*n);
-
- debug!("Creating rule node: {:p}", &*n);
-
- Self {
- p: UnsafeBox::from_box(n),
- }
- }
-
- unsafe fn from_unsafe_box(p: UnsafeBox<RuleNode>) -> Self {
- Self { p }
- }
-
- unsafe fn downgrade(&self) -> WeakRuleNode {
- WeakRuleNode {
- p: UnsafeBox::clone(&self.p),
- }
- }
-
- /// Get the parent rule node of this rule node.
- pub fn parent(&self) -> Option<&StrongRuleNode> {
- self.p.parent.as_ref()
- }
-
- pub(super) fn ensure_child(
- &self,
- root: &StrongRuleNode,
- source: StyleSource,
- cascade_priority: CascadePriority,
- ) -> StrongRuleNode {
- use parking_lot::RwLockUpgradableReadGuard;
-
- debug_assert!(
- self.p.cascade_priority <= cascade_priority,
- "Should be ordered (instead {:?} > {:?}), from {:?} and {:?}",
- self.p.cascade_priority,
- cascade_priority,
- self.p.source,
- source,
- );
-
- let key = ChildKey(cascade_priority, source.key());
- let children = self.p.children.upgradable_read();
- if let Some(child) = children.get(&key, |node| node.p.key()) {
- // Sound to call because we read-locked the parent's children.
- return unsafe { child.upgrade() };
- }
- let mut children = RwLockUpgradableReadGuard::upgrade(children);
- match children.entry(key, |node| node.p.key()) {
- Entry::Occupied(child) => {
- // Sound to call because we write-locked the parent's children.
- unsafe { child.upgrade() }
- },
- Entry::Vacant(entry) => unsafe {
- let node = StrongRuleNode::new(Box::new(RuleNode::new(
- root.downgrade(),
- self.clone(),
- source,
- cascade_priority,
- )));
- // Sound to call because we still own a strong reference to
- // this node, through the `node` variable itself that we are
- // going to return to the caller.
- entry.insert(node.downgrade());
- node
- },
- }
- }
-
- /// Get the style source corresponding to this rule node. May return `None`
- /// if it's the root node, which means that the node hasn't matched any
- /// rules.
- pub fn style_source(&self) -> Option<&StyleSource> {
- self.p.source.as_ref()
- }
-
- /// The cascade priority.
- #[inline]
- pub fn cascade_priority(&self) -> CascadePriority {
- self.p.cascade_priority
- }
-
- /// The cascade level.
- #[inline]
- pub fn cascade_level(&self) -> CascadeLevel {
- self.cascade_priority().cascade_level()
- }
-
- /// The importance.
- #[inline]
- pub fn importance(&self) -> crate::properties::Importance {
- self.cascade_level().importance()
- }
-
- /// Returns whether this node has any child, only intended for testing
- /// purposes.
- pub unsafe fn has_children_for_testing(&self) -> bool {
- !self.p.children.read().is_empty()
- }
-
- pub(super) fn dump<W: Write>(&self, guards: &StylesheetGuards, writer: &mut W, indent: usize) {
- const INDENT_INCREMENT: usize = 4;
-
- for _ in 0..indent {
- let _ = write!(writer, " ");
- }
-
- let _ = writeln!(
- writer,
- " - {:p} (ref: {:?}, parent: {:?})",
- &*self.p,
- self.p.refcount.load(Ordering::Relaxed),
- self.parent().map(|p| &*p.p as *const RuleNode)
- );
-
- for _ in 0..indent {
- let _ = write!(writer, " ");
- }
-
- if let Some(source) = self.style_source() {
- source.dump(self.cascade_level().guard(guards), writer);
- } else {
- if indent != 0 {
- warn!("How has this happened?");
- }
- let _ = write!(writer, "(root)");
- }
-
- let _ = write!(writer, "\n");
- for child in &*self.p.children.read() {
- unsafe {
- child
- .upgrade()
- .dump(guards, writer, indent + INDENT_INCREMENT);
- }
- }
- }
-}
-
-impl Clone for StrongRuleNode {
- fn clone(&self) -> Self {
- debug!(
- "{:p}: {:?}+",
- &*self.p,
- self.p.refcount.load(Ordering::Relaxed)
- );
- debug_assert!(self.p.refcount.load(Ordering::Relaxed) > 0);
- self.p.refcount.fetch_add(1, Ordering::Relaxed);
- unsafe { StrongRuleNode::from_unsafe_box(UnsafeBox::clone(&self.p)) }
- }
-}
-
-impl Drop for StrongRuleNode {
- #[cfg_attr(feature = "servo", allow(unused_mut))]
- fn drop(&mut self) {
- let node = &*self.p;
- debug!("{:p}: {:?}-", node, node.refcount.load(Ordering::Relaxed));
- debug!(
- "Dropping node: {:p}, root: {:?}, parent: {:?}",
- node,
- node.root.as_ref().map(|r| &*r.p as *const RuleNode),
- node.parent.as_ref().map(|p| &*p.p as *const RuleNode)
- );
-
- let should_drop = {
- debug_assert!(node.refcount.load(Ordering::Relaxed) > 0);
- node.refcount.fetch_sub(1, Ordering::Release) == 1
- };
-
- if !should_drop {
- // The refcount didn't even drop zero yet, there is nothing for us
- // to do anymore.
- return;
- }
-
- unsafe {
- if node.root.is_some() {
- // This is a non-root node and we just observed the refcount
- // dropping to zero, we need to pretend to be on the free list
- // to unstuck any thread who tried to resurrect this node first
- // through `WeakRuleNode::upgrade`.
- RuleNode::pretend_to_be_on_free_list(&self.p);
-
- // Attempt to push the node on the free list. This may fail
- // if the free list is gone.
- if RuleNode::push_on_free_list(&self.p) {
- return;
- }
- }
-
- // Either this was the last reference of the root node, or the
- // tree rule is gone and there is no free list anymore. Drop the
- // node.
- RuleNode::drop_without_free_list(&mut self.p);
- }
- }
-}
-
-impl WeakRuleNode {
- /// Upgrades this weak node reference, returning a strong one.
- ///
- /// Must be called with items stored in a node's children list. The children
- /// list must at least be read-locked when this is called.
- unsafe fn upgrade(&self) -> StrongRuleNode {
- debug!("Upgrading weak node: {:p}", &*self.p);
-
- if self.p.refcount.fetch_add(1, Ordering::Relaxed) == 0 {
- // We observed a refcount of 0, we need to wait for this node to
- // be put on the free list. Resetting the `next_free` pointer to
- // null is only done in `RuleNode::drop_without_free_list`, just
- // before a release refcount decrement, so this acquire fence here
- // makes sure that we observed the write to null before we loop
- // until there is a non-null value.
- atomic::fence(Ordering::Acquire);
- while self.p.next_free.load(Ordering::Relaxed).is_null() {}
- }
- StrongRuleNode::from_unsafe_box(UnsafeBox::clone(&self.p))
- }
-}
-
-impl fmt::Debug for StrongRuleNode {
- fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
- (&*self.p as *const RuleNode).fmt(f)
- }
-}
-
-impl Eq for StrongRuleNode {}
-impl PartialEq for StrongRuleNode {
- fn eq(&self, other: &Self) -> bool {
- &*self.p as *const RuleNode == &*other.p
- }
-}
-
-impl hash::Hash for StrongRuleNode {
- fn hash<H>(&self, state: &mut H)
- where
- H: hash::Hasher,
- {
- (&*self.p as *const RuleNode).hash(state)
- }
-}
-
-// Large pages generate thousands of RuleNode objects.
-size_of_test!(RuleNode, 80);
-// StrongRuleNode should be pointer-sized even inside an option.
-size_of_test!(Option<StrongRuleNode>, 8);
diff --git a/components/style/rule_tree/level.rs b/components/style/rule_tree/level.rs
deleted file mode 100644
index b8cbe55ed9c..00000000000
--- a/components/style/rule_tree/level.rs
+++ /dev/null
@@ -1,249 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
-
-#![forbid(unsafe_code)]
-
-use crate::properties::Importance;
-use crate::shared_lock::{SharedRwLockReadGuard, StylesheetGuards};
-use crate::stylesheets::Origin;
-
-/// The cascade level these rules are relevant at, as per[1][2][3].
-///
-/// Presentational hints for SVG and HTML are in the "author-level
-/// zero-specificity" level, that is, right after user rules, and before author
-/// rules.
-///
-/// The order of variants declared here is significant, and must be in
-/// _ascending_ order of precedence.
-///
-/// See also [4] for the Shadow DOM bits. We rely on the invariant that rules
-/// from outside the tree the element is in can't affect the element.
-///
-/// The opposite is not true (i.e., :host and ::slotted) from an "inner" shadow
-/// tree may affect an element connected to the document or an "outer" shadow
-/// tree.
-///
-/// [1]: https://drafts.csswg.org/css-cascade/#cascade-origin
-/// [2]: https://drafts.csswg.org/css-cascade/#preshint
-/// [3]: https://html.spec.whatwg.org/multipage/#presentational-hints
-/// [4]: https://drafts.csswg.org/css-scoping/#shadow-cascading
-#[repr(u8)]
-#[derive(Clone, Copy, Debug, Eq, Hash, MallocSizeOf, Ord, PartialEq, PartialOrd)]
-pub enum CascadeLevel {
- /// Normal User-Agent rules.
- UANormal,
- /// User normal rules.
- UserNormal,
- /// Presentational hints.
- PresHints,
- /// Shadow DOM styles from author styles.
- AuthorNormal {
- /// The order in the shadow tree hierarchy. This number is relative to
- /// the tree of the element, and thus the only invariants that need to
- /// be preserved is:
- ///
- /// * Zero is the same tree as the element that matched the rule. This
- /// is important so that we can optimize style attribute insertions.
- ///
- /// * The levels are ordered in accordance with
- /// https://drafts.csswg.org/css-scoping/#shadow-cascading
- shadow_cascade_order: ShadowCascadeOrder,
- },
- /// SVG SMIL animations.
- SMILOverride,
- /// CSS animations and script-generated animations.
- Animations,
- /// Author-supplied important rules.
- AuthorImportant {
- /// The order in the shadow tree hierarchy, inverted, so that PartialOrd
- /// does the right thing.
- shadow_cascade_order: ShadowCascadeOrder,
- },
- /// User important rules.
- UserImportant,
- /// User-agent important rules.
- UAImportant,
- /// Transitions
- Transitions,
-}
-
-impl CascadeLevel {
- /// Convert this level from "unimportant" to "important".
- pub fn important(&self) -> Self {
- match *self {
- Self::UANormal => Self::UAImportant,
- Self::UserNormal => Self::UserImportant,
- Self::AuthorNormal {
- shadow_cascade_order,
- } => Self::AuthorImportant {
- shadow_cascade_order: -shadow_cascade_order,
- },
- Self::PresHints |
- Self::SMILOverride |
- Self::Animations |
- Self::AuthorImportant { .. } |
- Self::UserImportant |
- Self::UAImportant |
- Self::Transitions => *self,
- }
- }
-
- /// Convert this level from "important" to "non-important".
- pub fn unimportant(&self) -> Self {
- match *self {
- Self::UAImportant => Self::UANormal,
- Self::UserImportant => Self::UserNormal,
- Self::AuthorImportant {
- shadow_cascade_order,
- } => Self::AuthorNormal {
- shadow_cascade_order: -shadow_cascade_order,
- },
- Self::PresHints |
- Self::SMILOverride |
- Self::Animations |
- Self::AuthorNormal { .. } |
- Self::UserNormal |
- Self::UANormal |
- Self::Transitions => *self,
- }
- }
-
- /// Select a lock guard for this level
- pub fn guard<'a>(&self, guards: &'a StylesheetGuards<'a>) -> &'a SharedRwLockReadGuard<'a> {
- match *self {
- Self::UANormal | Self::UserNormal | Self::UserImportant | Self::UAImportant => {
- guards.ua_or_user
- },
- _ => guards.author,
- }
- }
-
- /// Returns the cascade level for author important declarations from the
- /// same tree as the element.
- #[inline]
- pub fn same_tree_author_important() -> Self {
- Self::AuthorImportant {
- shadow_cascade_order: ShadowCascadeOrder::for_same_tree(),
- }
- }
-
- /// Returns the cascade level for author normal declarations from the same
- /// tree as the element.
- #[inline]
- pub fn same_tree_author_normal() -> Self {
- Self::AuthorNormal {
- shadow_cascade_order: ShadowCascadeOrder::for_same_tree(),
- }
- }
-
- /// Returns whether this cascade level represents important rules of some
- /// sort.
- #[inline]
- pub fn is_important(&self) -> bool {
- match *self {
- Self::AuthorImportant { .. } | Self::UserImportant | Self::UAImportant => true,
- _ => false,
- }
- }
-
- /// Returns the importance relevant for this rule. Pretty similar to
- /// `is_important`.
- #[inline]
- pub fn importance(&self) -> Importance {
- if self.is_important() {
- Importance::Important
- } else {
- Importance::Normal
- }
- }
-
- /// Returns the cascade origin of the rule.
- #[inline]
- pub fn origin(&self) -> Origin {
- match *self {
- Self::UAImportant | Self::UANormal => Origin::UserAgent,
- Self::UserImportant | Self::UserNormal => Origin::User,
- Self::PresHints |
- Self::AuthorNormal { .. } |
- Self::AuthorImportant { .. } |
- Self::SMILOverride |
- Self::Animations |
- Self::Transitions => Origin::Author,
- }
- }
-
- /// Returns whether this cascade level represents an animation rules.
- #[inline]
- pub fn is_animation(&self) -> bool {
- match *self {
- Self::SMILOverride | Self::Animations | Self::Transitions => true,
- _ => false,
- }
- }
-}
-
-/// A counter to track how many shadow root rules deep we are. This is used to
-/// handle:
-///
-/// https://drafts.csswg.org/css-scoping/#shadow-cascading
-///
-/// See the static functions for the meaning of different values.
-#[derive(Clone, Copy, Debug, Eq, Hash, MallocSizeOf, Ord, PartialEq, PartialOrd)]
-pub struct ShadowCascadeOrder(i8);
-
-impl ShadowCascadeOrder {
- /// We keep a maximum of 3 bits of order as a limit so that we can pack
- /// CascadeLevel in one byte by using half of it for the order, if that ends
- /// up being necessary.
- const MAX: i8 = 0b111;
- const MIN: i8 = -Self::MAX;
-
- /// A level for the outermost shadow tree (the shadow tree we own, and the
- /// ones from the slots we're slotted in).
- #[inline]
- pub fn for_outermost_shadow_tree() -> Self {
- Self(-1)
- }
-
- /// A level for the element's tree.
- #[inline]
- fn for_same_tree() -> Self {
- Self(0)
- }
-
- /// A level for the innermost containing tree (the one closest to the
- /// element).
- #[inline]
- pub fn for_innermost_containing_tree() -> Self {
- Self(1)
- }
-
- /// Decrement the level, moving inwards. We should only move inwards if
- /// we're traversing slots.
- #[inline]
- pub fn dec(&mut self) {
- debug_assert!(self.0 < 0);
- if self.0 != Self::MIN {
- self.0 -= 1;
- }
- }
-
- /// The level, moving inwards. We should only move inwards if we're
- /// traversing slots.
- #[inline]
- pub fn inc(&mut self) {
- debug_assert_ne!(self.0, -1);
- if self.0 != Self::MAX {
- self.0 += 1;
- }
- }
-}
-
-impl std::ops::Neg for ShadowCascadeOrder {
- type Output = Self;
- #[inline]
- fn neg(self) -> Self {
- Self(self.0.neg())
- }
-}
diff --git a/components/style/rule_tree/map.rs b/components/style/rule_tree/map.rs
deleted file mode 100644
index 33c470e9c12..00000000000
--- a/components/style/rule_tree/map.rs
+++ /dev/null
@@ -1,201 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
-
-#![forbid(unsafe_code)]
-
-use fxhash::FxHashMap;
-use malloc_size_of::{MallocShallowSizeOf, MallocSizeOfOps};
-use std::collections::hash_map;
-use std::hash::Hash;
-use std::mem;
-
-pub(super) struct Map<K, V> {
- inner: MapInner<K, V>,
-}
-
-enum MapInner<K, V> {
- Empty,
- One(V),
- Map(Box<FxHashMap<K, V>>),
-}
-
-pub(super) struct MapIter<'a, K, V> {
- inner: MapIterInner<'a, K, V>,
-}
-
-enum MapIterInner<'a, K, V> {
- One(std::option::IntoIter<&'a V>),
- Map(std::collections::hash_map::Values<'a, K, V>),
-}
-
-pub(super) enum Entry<'a, K, V> {
- Occupied(&'a mut V),
- Vacant(VacantEntry<'a, K, V>),
-}
-
-pub(super) struct VacantEntry<'a, K, V> {
- inner: VacantEntryInner<'a, K, V>,
-}
-
-enum VacantEntryInner<'a, K, V> {
- One(&'a mut MapInner<K, V>),
- Map(hash_map::VacantEntry<'a, K, V>),
-}
-
-impl<K, V> Default for Map<K, V> {
- fn default() -> Self {
- Map {
- inner: MapInner::Empty,
- }
- }
-}
-
-impl<'a, K, V> IntoIterator for &'a Map<K, V> {
- type Item = &'a V;
- type IntoIter = MapIter<'a, K, V>;
-
- fn into_iter(self) -> Self::IntoIter {
- MapIter {
- inner: match &self.inner {
- MapInner::Empty => MapIterInner::One(None.into_iter()),
- MapInner::One(one) => MapIterInner::One(Some(one).into_iter()),
- MapInner::Map(map) => MapIterInner::Map(map.values()),
- },
- }
- }
-}
-
-impl<'a, K, V> Iterator for MapIter<'a, K, V> {
- type Item = &'a V;
-
- fn next(&mut self) -> Option<Self::Item> {
- match &mut self.inner {
- MapIterInner::One(one_iter) => one_iter.next(),
- MapIterInner::Map(map_iter) => map_iter.next(),
- }
- }
-}
-
-impl<K, V> Map<K, V>
-where
- K: Eq + Hash,
-{
- pub(super) fn is_empty(&self) -> bool {
- match &self.inner {
- MapInner::Empty => true,
- MapInner::One(_) => false,
- MapInner::Map(map) => map.is_empty(),
- }
- }
-
- #[cfg(debug_assertions)]
- pub(super) fn len(&self) -> usize {
- match &self.inner {
- MapInner::Empty => 0,
- MapInner::One(_) => 1,
- MapInner::Map(map) => map.len(),
- }
- }
-
- pub(super) fn get(&self, key: &K, key_from_value: impl FnOnce(&V) -> K) -> Option<&V> {
- match &self.inner {
- MapInner::One(one) if *key == key_from_value(one) => Some(one),
- MapInner::Map(map) => map.get(key),
- MapInner::Empty | MapInner::One(_) => None,
- }
- }
-
- pub(super) fn entry(
- &mut self,
- key: K,
- key_from_value: impl FnOnce(&V) -> K,
- ) -> Entry<'_, K, V> {
- match self.inner {
- ref mut inner @ MapInner::Empty => Entry::Vacant(VacantEntry {
- inner: VacantEntryInner::One(inner),
- }),
- MapInner::One(_) => {
- let one = match mem::replace(&mut self.inner, MapInner::Empty) {
- MapInner::One(one) => one,
- _ => unreachable!(),
- };
- // If this panics, the child `one` will be lost.
- let one_key = key_from_value(&one);
- // Same for the equality test.
- if key == one_key {
- self.inner = MapInner::One(one);
- let one = match &mut self.inner {
- MapInner::One(one) => one,
- _ => unreachable!(),
- };
- return Entry::Occupied(one);
- }
- self.inner = MapInner::Map(Box::new(FxHashMap::with_capacity_and_hasher(
- 2,
- Default::default(),
- )));
- let map = match &mut self.inner {
- MapInner::Map(map) => map,
- _ => unreachable!(),
- };
- map.insert(one_key, one);
- match map.entry(key) {
- hash_map::Entry::Vacant(entry) => Entry::Vacant(VacantEntry {
- inner: VacantEntryInner::Map(entry),
- }),
- _ => unreachable!(),
- }
- },
- MapInner::Map(ref mut map) => match map.entry(key) {
- hash_map::Entry::Occupied(entry) => Entry::Occupied(entry.into_mut()),
- hash_map::Entry::Vacant(entry) => Entry::Vacant(VacantEntry {
- inner: VacantEntryInner::Map(entry),
- }),
- },
- }
- }
-
- pub(super) fn remove(&mut self, key: &K, key_from_value: impl FnOnce(&V) -> K) -> Option<V> {
- match &mut self.inner {
- MapInner::One(one) if *key == key_from_value(one) => {
- match mem::replace(&mut self.inner, MapInner::Empty) {
- MapInner::One(one) => Some(one),
- _ => unreachable!(),
- }
- },
- MapInner::Map(map) => map.remove(key),
- MapInner::Empty | MapInner::One(_) => None,
- }
- }
-}
-
-impl<'a, K, V> VacantEntry<'a, K, V> {
- pub(super) fn insert(self, value: V) -> &'a mut V {
- match self.inner {
- VacantEntryInner::One(map) => {
- *map = MapInner::One(value);
- match map {
- MapInner::One(one) => one,
- _ => unreachable!(),
- }
- },
- VacantEntryInner::Map(entry) => entry.insert(value),
- }
- }
-}
-
-impl<K, V> MallocShallowSizeOf for Map<K, V>
-where
- K: Eq + Hash,
-{
- fn shallow_size_of(&self, ops: &mut MallocSizeOfOps) -> usize {
- match &self.inner {
- MapInner::Map(m) => {
- // We want to account for both the box and the hashmap.
- m.shallow_size_of(ops) + (**m).shallow_size_of(ops)
- },
- MapInner::One(_) | MapInner::Empty => 0,
- }
- }
-}
diff --git a/components/style/rule_tree/mod.rs b/components/style/rule_tree/mod.rs
deleted file mode 100644
index 01510e62070..00000000000
--- a/components/style/rule_tree/mod.rs
+++ /dev/null
@@ -1,426 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
-
-#![deny(unsafe_code)]
-
-//! The rule tree.
-
-use crate::applicable_declarations::{ApplicableDeclarationList, CascadePriority};
-use crate::properties::{LonghandIdSet, PropertyDeclarationBlock};
-use crate::shared_lock::{Locked, StylesheetGuards};
-use crate::stylesheets::layer_rule::LayerOrder;
-use servo_arc::{Arc, ArcBorrow};
-use smallvec::SmallVec;
-use std::io::{self, Write};
-
-mod core;
-mod level;
-mod map;
-mod source;
-mod unsafe_box;
-
-pub use self::core::{RuleTree, StrongRuleNode};
-pub use self::level::{CascadeLevel, ShadowCascadeOrder};
-pub use self::source::StyleSource;
-
-impl RuleTree {
- fn dump<W: Write>(&self, guards: &StylesheetGuards, writer: &mut W) {
- let _ = writeln!(writer, " + RuleTree");
- self.root().dump(guards, writer, 0);
- }
-
- /// Dump the rule tree to stdout.
- pub fn dump_stdout(&self, guards: &StylesheetGuards) {
- let mut stdout = io::stdout();
- self.dump(guards, &mut stdout);
- }
-
- /// Inserts the given rules, that must be in proper order by specifity, and
- /// returns the corresponding rule node representing the last inserted one.
- ///
- /// !important rules are detected and inserted into the appropriate position
- /// in the rule tree. This allows selector matching to ignore importance,
- /// while still maintaining the appropriate cascade order in the rule tree.
- pub fn insert_ordered_rules_with_important<'a, I>(
- &self,
- iter: I,
- guards: &StylesheetGuards,
- ) -> StrongRuleNode
- where
- I: Iterator<Item = (StyleSource, CascadePriority)>,
- {
- use self::CascadeLevel::*;
- let mut current = self.root().clone();
-
- let mut found_important = false;
-
- let mut important_author = SmallVec::<[(StyleSource, CascadePriority); 4]>::new();
- let mut important_user = SmallVec::<[(StyleSource, CascadePriority); 4]>::new();
- let mut important_ua = SmallVec::<[(StyleSource, CascadePriority); 4]>::new();
- let mut transition = None;
-
- for (source, priority) in iter {
- let level = priority.cascade_level();
- debug_assert!(!level.is_important(), "Important levels handled internally");
-
- let any_important = {
- let pdb = source.read(level.guard(guards));
- pdb.any_important()
- };
-
- if any_important {
- found_important = true;
- match level {
- AuthorNormal { .. } => {
- important_author.push((source.clone(), priority.important()))
- },
- UANormal => important_ua.push((source.clone(), priority.important())),
- UserNormal => important_user.push((source.clone(), priority.important())),
- _ => {},
- };
- }
-
- // We don't optimize out empty rules, even though we could.
- //
- // Inspector relies on every rule being inserted in the normal level
- // at least once, in order to return the rules with the correct
- // specificity order.
- //
- // TODO(emilio): If we want to apply these optimizations without
- // breaking inspector's expectations, we'd need to run
- // selector-matching again at the inspector's request. That may or
- // may not be a better trade-off.
- if matches!(level, Transitions) && found_important {
- // There can be at most one transition, and it will come at
- // the end of the iterator. Stash it and apply it after
- // !important rules.
- debug_assert!(transition.is_none());
- transition = Some(source);
- } else {
- current = current.ensure_child(self.root(), source, priority);
- }
- }
-
- // Early-return in the common case of no !important declarations.
- if !found_important {
- return current;
- }
-
- // Insert important declarations, in order of increasing importance,
- // followed by any transition rule.
- //
- // Important rules are sorted differently from unimportant ones by
- // shadow order and cascade order.
- if !important_author.is_empty() &&
- important_author.first().unwrap().1 != important_author.last().unwrap().1
- {
- // We only need to sort if the important rules come from
- // different trees, but we need this sort to be stable.
- //
- // FIXME(emilio): This could maybe be smarter, probably by chunking
- // the important rules while inserting, and iterating the outer
- // chunks in reverse order.
- //
- // That is, if we have rules with levels like: -1 -1 -1 0 0 0 1 1 1,
- // we're really only sorting the chunks, while keeping elements
- // inside the same chunk already sorted. Seems like we could try to
- // keep a SmallVec-of-SmallVecs with the chunks and just iterate the
- // outer in reverse.
- important_author.sort_by_key(|&(_, priority)| priority);
- }
-
- for (source, priority) in important_author.drain(..) {
- current = current.ensure_child(self.root(), source, priority);
- }
-
- for (source, priority) in important_user.drain(..) {
- current = current.ensure_child(self.root(), source, priority);
- }
-
- for (source, priority) in important_ua.drain(..) {
- current = current.ensure_child(self.root(), source, priority);
- }
-
- if let Some(source) = transition {
- current = current.ensure_child(
- self.root(),
- source,
- CascadePriority::new(Transitions, LayerOrder::root()),
- );
- }
-
- current
- }
-
- /// Given a list of applicable declarations, insert the rules and return the
- /// corresponding rule node.
- pub fn compute_rule_node(
- &self,
- applicable_declarations: &mut ApplicableDeclarationList,
- guards: &StylesheetGuards,
- ) -> StrongRuleNode {
- self.insert_ordered_rules_with_important(
- applicable_declarations.drain(..).map(|d| d.for_rule_tree()),
- guards,
- )
- }
-
- /// Insert the given rules, that must be in proper order by specifity, and
- /// return the corresponding rule node representing the last inserted one.
- pub fn insert_ordered_rules<'a, I>(&self, iter: I) -> StrongRuleNode
- where
- I: Iterator<Item = (StyleSource, CascadePriority)>,
- {
- self.insert_ordered_rules_from(self.root().clone(), iter)
- }
-
- fn insert_ordered_rules_from<'a, I>(&self, from: StrongRuleNode, iter: I) -> StrongRuleNode
- where
- I: Iterator<Item = (StyleSource, CascadePriority)>,
- {
- let mut current = from;
- for (source, priority) in iter {
- current = current.ensure_child(self.root(), source, priority);
- }
- current
- }
-
- /// Replaces a rule in a given level (if present) for another rule.
- ///
- /// Returns the resulting node that represents the new path, or None if
- /// the old path is still valid.
- pub fn update_rule_at_level(
- &self,
- level: CascadeLevel,
- layer_order: LayerOrder,
- pdb: Option<ArcBorrow<Locked<PropertyDeclarationBlock>>>,
- path: &StrongRuleNode,
- guards: &StylesheetGuards,
- important_rules_changed: &mut bool,
- ) -> Option<StrongRuleNode> {
- // TODO(emilio): Being smarter with lifetimes we could avoid a bit of
- // the refcount churn.
- let mut current = path.clone();
- *important_rules_changed = false;
-
- // First walk up until the first less-or-equally specific rule.
- let mut children = SmallVec::<[_; 10]>::new();
- while current.cascade_priority().cascade_level() > level {
- children.push((
- current.style_source().unwrap().clone(),
- current.cascade_priority(),
- ));
- current = current.parent().unwrap().clone();
- }
-
- // Then remove the one at the level we want to replace, if any.
- //
- // NOTE: Here we assume that only one rule can be at the level we're
- // replacing.
- //
- // This is certainly true for HTML style attribute rules, animations and
- // transitions, but could not be so for SMIL animations, which we'd need
- // to special-case (isn't hard, it's just about removing the `if` and
- // special cases, and replacing them for a `while` loop, avoiding the
- // optimizations).
- if current.cascade_priority().cascade_level() == level {
- *important_rules_changed |= level.is_important();
-
- let current_decls = current.style_source().unwrap().as_declarations();
-
- // If the only rule at the level we're replacing is exactly the
- // same as `pdb`, we're done, and `path` is still valid.
- if let (Some(ref pdb), Some(ref current_decls)) = (pdb, current_decls) {
- // If the only rule at the level we're replacing is exactly the
- // same as `pdb`, we're done, and `path` is still valid.
- //
- // TODO(emilio): Another potential optimization is the one where
- // we can just replace the rule at that level for `pdb`, and
- // then we don't need to re-create the children, and `path` is
- // also equally valid. This is less likely, and would require an
- // in-place mutation of the source, which is, at best, fiddly,
- // so let's skip it for now.
- let is_here_already = ArcBorrow::ptr_eq(pdb, current_decls);
- if is_here_already {
- debug!("Picking the fast path in rule replacement");
- return None;
- }
- }
-
- if current_decls.is_some() {
- current = current.parent().unwrap().clone();
- }
- }
-
- // Insert the rule if it's relevant at this level in the cascade.
- //
- // These optimizations are likely to be important, because the levels
- // where replacements apply (style and animations) tend to trigger
- // pretty bad styling cases already.
- if let Some(pdb) = pdb {
- if level.is_important() {
- if pdb.read_with(level.guard(guards)).any_important() {
- current = current.ensure_child(
- self.root(),
- StyleSource::from_declarations(pdb.clone_arc()),
- CascadePriority::new(level, layer_order),
- );
- *important_rules_changed = true;
- }
- } else {
- if pdb.read_with(level.guard(guards)).any_normal() {
- current = current.ensure_child(
- self.root(),
- StyleSource::from_declarations(pdb.clone_arc()),
- CascadePriority::new(level, layer_order),
- );
- }
- }
- }
-
- // Now the rule is in the relevant place, push the children as
- // necessary.
- let rule = self.insert_ordered_rules_from(current, children.drain(..).rev());
- Some(rule)
- }
-
- /// Returns new rule nodes without Transitions level rule.
- pub fn remove_transition_rule_if_applicable(&self, path: &StrongRuleNode) -> StrongRuleNode {
- // Return a clone if there is no transition level.
- if path.cascade_level() != CascadeLevel::Transitions {
- return path.clone();
- }
-
- path.parent().unwrap().clone()
- }
-
- /// Returns new rule node without rules from declarative animations.
- pub fn remove_animation_rules(&self, path: &StrongRuleNode) -> StrongRuleNode {
- // Return a clone if there are no animation rules.
- if !path.has_animation_or_transition_rules() {
- return path.clone();
- }
-
- let iter = path
- .self_and_ancestors()
- .take_while(|node| node.cascade_level() >= CascadeLevel::SMILOverride);
- let mut last = path;
- let mut children = SmallVec::<[_; 10]>::new();
- for node in iter {
- if !node.cascade_level().is_animation() {
- children.push((
- node.style_source().unwrap().clone(),
- node.cascade_priority(),
- ));
- }
- last = node;
- }
-
- let rule = self
- .insert_ordered_rules_from(last.parent().unwrap().clone(), children.drain(..).rev());
- rule
- }
-
- /// Returns new rule node by adding animation rules at transition level.
- /// The additional rules must be appropriate for the transition
- /// level of the cascade, which is the highest level of the cascade.
- /// (This is the case for one current caller, the cover rule used
- /// for CSS transitions.)
- pub fn add_animation_rules_at_transition_level(
- &self,
- path: &StrongRuleNode,
- pdb: Arc<Locked<PropertyDeclarationBlock>>,
- guards: &StylesheetGuards,
- ) -> StrongRuleNode {
- let mut dummy = false;
- self.update_rule_at_level(
- CascadeLevel::Transitions,
- LayerOrder::root(),
- Some(pdb.borrow_arc()),
- path,
- guards,
- &mut dummy,
- )
- .expect("Should return a valid rule node")
- }
-}
-
-impl StrongRuleNode {
- /// Get an iterator for this rule node and its ancestors.
- pub fn self_and_ancestors(&self) -> SelfAndAncestors {
- SelfAndAncestors {
- current: Some(self),
- }
- }
-
- /// Returns true if there is either animation or transition level rule.
- pub fn has_animation_or_transition_rules(&self) -> bool {
- self.self_and_ancestors()
- .take_while(|node| node.cascade_level() >= CascadeLevel::SMILOverride)
- .any(|node| node.cascade_level().is_animation())
- }
-
- /// Get a set of properties whose CascadeLevel are higher than Animations
- /// but not equal to Transitions.
- ///
- /// If there are any custom properties, we set the boolean value of the
- /// returned tuple to true.
- pub fn get_properties_overriding_animations(
- &self,
- guards: &StylesheetGuards,
- ) -> (LonghandIdSet, bool) {
- use crate::properties::PropertyDeclarationId;
-
- // We want to iterate over cascade levels that override the animations
- // level, i.e. !important levels and the transitions level.
- //
- // However, we actually want to skip the transitions level because
- // although it is higher in the cascade than animations, when both
- // transitions and animations are present for a given element and
- // property, transitions are suppressed so that they don't actually
- // override animations.
- let iter = self
- .self_and_ancestors()
- .skip_while(|node| node.cascade_level() == CascadeLevel::Transitions)
- .take_while(|node| node.cascade_level() > CascadeLevel::Animations);
- let mut result = (LonghandIdSet::new(), false);
- for node in iter {
- let style = node.style_source().unwrap();
- for (decl, important) in style
- .read(node.cascade_level().guard(guards))
- .declaration_importance_iter()
- {
- // Although we are only iterating over cascade levels that
- // override animations, in a given property declaration block we
- // can have a mixture of !important and non-!important
- // declarations but only the !important declarations actually
- // override animations.
- if important.important() {
- match decl.id() {
- PropertyDeclarationId::Longhand(id) => result.0.insert(id),
- PropertyDeclarationId::Custom(_) => result.1 = true,
- }
- }
- }
- }
- result
- }
-}
-
-/// An iterator over a rule node and its ancestors.
-#[derive(Clone)]
-pub struct SelfAndAncestors<'a> {
- current: Option<&'a StrongRuleNode>,
-}
-
-impl<'a> Iterator for SelfAndAncestors<'a> {
- type Item = &'a StrongRuleNode;
-
- fn next(&mut self) -> Option<Self::Item> {
- self.current.map(|node| {
- self.current = node.parent();
- node
- })
- }
-}
diff --git a/components/style/rule_tree/source.rs b/components/style/rule_tree/source.rs
deleted file mode 100644
index 76443692d74..00000000000
--- a/components/style/rule_tree/source.rs
+++ /dev/null
@@ -1,75 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
-
-#![forbid(unsafe_code)]
-
-use crate::properties::PropertyDeclarationBlock;
-use crate::shared_lock::{Locked, SharedRwLockReadGuard};
-use crate::stylesheets::StyleRule;
-use servo_arc::{Arc, ArcBorrow, ArcUnion, ArcUnionBorrow};
-use std::io::Write;
-use std::ptr;
-
-/// A style source for the rule node. It can either be a CSS style rule or a
-/// declaration block.
-///
-/// Note that, even though the declaration block from inside the style rule
-/// could be enough to implement the rule tree, keeping the whole rule provides
-/// more debuggability, and also the ability of show those selectors to
-/// devtools.
-#[derive(Clone, Debug)]
-pub struct StyleSource(ArcUnion<Locked<StyleRule>, Locked<PropertyDeclarationBlock>>);
-
-impl PartialEq for StyleSource {
- fn eq(&self, other: &Self) -> bool {
- ArcUnion::ptr_eq(&self.0, &other.0)
- }
-}
-
-impl StyleSource {
- /// Creates a StyleSource from a StyleRule.
- pub fn from_rule(rule: Arc<Locked<StyleRule>>) -> Self {
- StyleSource(ArcUnion::from_first(rule))
- }
-
- #[inline]
- pub(super) fn key(&self) -> ptr::NonNull<()> {
- self.0.ptr()
- }
-
- /// Creates a StyleSource from a PropertyDeclarationBlock.
- pub fn from_declarations(decls: Arc<Locked<PropertyDeclarationBlock>>) -> Self {
- StyleSource(ArcUnion::from_second(decls))
- }
-
- pub(super) fn dump<W: Write>(&self, guard: &SharedRwLockReadGuard, writer: &mut W) {
- if let Some(ref rule) = self.0.as_first() {
- let rule = rule.read_with(guard);
- let _ = write!(writer, "{:?}", rule.selectors);
- }
-
- let _ = write!(writer, " -> {:?}", self.read(guard).declarations());
- }
-
- /// Read the style source guard, and obtain thus read access to the
- /// underlying property declaration block.
- #[inline]
- pub fn read<'a>(&'a self, guard: &'a SharedRwLockReadGuard) -> &'a PropertyDeclarationBlock {
- let block: &Locked<PropertyDeclarationBlock> = match self.0.borrow() {
- ArcUnionBorrow::First(ref rule) => &rule.get().read_with(guard).block,
- ArcUnionBorrow::Second(ref block) => block.get(),
- };
- block.read_with(guard)
- }
-
- /// Returns the style rule if applicable, otherwise None.
- pub fn as_rule(&self) -> Option<ArcBorrow<Locked<StyleRule>>> {
- self.0.as_first()
- }
-
- /// Returns the declaration block if applicable, otherwise None.
- pub fn as_declarations(&self) -> Option<ArcBorrow<Locked<PropertyDeclarationBlock>>> {
- self.0.as_second()
- }
-}
diff --git a/components/style/rule_tree/unsafe_box.rs b/components/style/rule_tree/unsafe_box.rs
deleted file mode 100644
index eaa441d7b25..00000000000
--- a/components/style/rule_tree/unsafe_box.rs
+++ /dev/null
@@ -1,74 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
-
-#![allow(unsafe_code)]
-
-use std::mem::ManuallyDrop;
-use std::ops::Deref;
-use std::ptr;
-
-/// An unsafe box, derefs to `T`.
-pub(super) struct UnsafeBox<T> {
- inner: ManuallyDrop<Box<T>>,
-}
-
-impl<T> UnsafeBox<T> {
- /// Creates a new unsafe box.
- pub(super) fn from_box(value: Box<T>) -> Self {
- Self {
- inner: ManuallyDrop::new(value),
- }
- }
-
- /// Creates a new box from a pointer.
- ///
- /// # Safety
- ///
- /// The input should point to a valid `T`.
- pub(super) unsafe fn from_raw(ptr: *mut T) -> Self {
- Self {
- inner: ManuallyDrop::new(Box::from_raw(ptr)),
- }
- }
-
- /// Creates a new unsafe box from an existing one.
- ///
- /// # Safety
- ///
- /// There is no refcounting or whatever else in an unsafe box, so this
- /// operation can lead to double frees.
- pub(super) unsafe fn clone(this: &Self) -> Self {
- Self {
- inner: ptr::read(&this.inner),
- }
- }
-
- /// Returns a mutable reference to the inner value of this unsafe box.
- ///
- /// # Safety
- ///
- /// Given `Self::clone`, nothing prevents anyone from creating
- /// multiple mutable references to the inner value, which is completely UB.
- pub(crate) unsafe fn deref_mut(this: &mut Self) -> &mut T {
- &mut this.inner
- }
-
- /// Drops the inner value of this unsafe box.
- ///
- /// # Safety
- ///
- /// Given this doesn't consume the unsafe box itself, this has the same
- /// safety caveats as `ManuallyDrop::drop`.
- pub(super) unsafe fn drop(this: &mut Self) {
- ManuallyDrop::drop(&mut this.inner)
- }
-}
-
-impl<T> Deref for UnsafeBox<T> {
- type Target = T;
-
- fn deref(&self) -> &Self::Target {
- &self.inner
- }
-}
diff --git a/components/style/rustfmt.toml b/components/style/rustfmt.toml
deleted file mode 100644
index c7ad93bafe3..00000000000
--- a/components/style/rustfmt.toml
+++ /dev/null
@@ -1 +0,0 @@
-disable_all_formatting = true
diff --git a/components/style/scoped_tls.rs b/components/style/scoped_tls.rs
deleted file mode 100644
index 672cc275e65..00000000000
--- a/components/style/scoped_tls.rs
+++ /dev/null
@@ -1,75 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
-
-//! Stack-scoped thread-local storage for rayon thread pools.
-
-#![allow(unsafe_code)]
-#![deny(missing_docs)]
-
-use crate::global_style_data::STYLO_MAX_THREADS;
-use rayon;
-use std::cell::{Ref, RefCell, RefMut};
-use std::ops::DerefMut;
-
-/// A scoped TLS set, that is alive during the `'scope` lifetime.
-///
-/// We use this on Servo to construct thread-local contexts, but clear them once
-/// we're done with restyling.
-///
-/// Note that the cleanup is done on the thread that owns the scoped TLS, thus
-/// the Send bound.
-pub struct ScopedTLS<'scope, T: Send> {
- pool: &'scope rayon::ThreadPool,
- slots: [RefCell<Option<T>>; STYLO_MAX_THREADS],
-}
-
-/// The scoped TLS is `Sync` because no more than one worker thread can access a
-/// given slot.
-unsafe impl<'scope, T: Send> Sync for ScopedTLS<'scope, T> {}
-
-impl<'scope, T: Send> ScopedTLS<'scope, T> {
- /// Create a new scoped TLS that will last as long as this rayon threadpool
- /// reference.
- pub fn new(pool: &'scope rayon::ThreadPool) -> Self {
- debug_assert!(pool.current_num_threads() <= STYLO_MAX_THREADS);
- ScopedTLS {
- pool,
- slots: Default::default(),
- }
- }
-
- /// Return an immutable reference to the `Option<T>` that this thread owns.
- pub fn borrow(&self) -> Ref<Option<T>> {
- let idx = self.pool.current_thread_index().unwrap();
- self.slots[idx].borrow()
- }
-
- /// Return a mutable reference to the `Option<T>` that this thread owns.
- pub fn borrow_mut(&self) -> RefMut<Option<T>> {
- let idx = self.pool.current_thread_index().unwrap();
- self.slots[idx].borrow_mut()
- }
-
- /// Ensure that the current data this thread owns is initialized, or
- /// initialize it using `f`. We want ensure() to be fast and inline, and we
- /// want to inline the memmove that initializes the Option<T>. But we don't
- /// want to inline space for the entire large T struct in our stack frame.
- /// That's why we hand `f` a mutable borrow to write to instead of just
- /// having it return a T.
- #[inline(always)]
- pub fn ensure<F: FnOnce(&mut Option<T>)>(&self, f: F) -> RefMut<T> {
- let mut opt = self.borrow_mut();
- if opt.is_none() {
- f(opt.deref_mut());
- }
-
- RefMut::map(opt, |x| x.as_mut().unwrap())
- }
-
- /// Returns the slots. Safe because if we have a mut reference the tls can't be referenced by
- /// any other thread.
- pub fn slots(&mut self) -> &mut [RefCell<Option<T>>] {
- &mut self.slots
- }
-}
diff --git a/components/style/selector_map.rs b/components/style/selector_map.rs
deleted file mode 100644
index fe0b6238621..00000000000
--- a/components/style/selector_map.rs
+++ /dev/null
@@ -1,868 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
-
-//! A data structure to efficiently index structs containing selectors by local
-//! name, ids and hash.
-
-use crate::applicable_declarations::ApplicableDeclarationList;
-use crate::context::QuirksMode;
-use crate::dom::TElement;
-use crate::rule_tree::CascadeLevel;
-use crate::selector_parser::SelectorImpl;
-use crate::stylist::{CascadeData, ContainerConditionId, Rule, Stylist};
-use crate::AllocErr;
-use crate::{Atom, LocalName, Namespace, ShrinkIfNeeded, WeakAtom};
-use precomputed_hash::PrecomputedHash;
-use selectors::matching::{matches_selector, MatchingContext};
-use selectors::parser::{Combinator, Component, SelectorIter};
-use smallvec::SmallVec;
-use std::collections::hash_map;
-use std::collections::{HashMap, HashSet};
-use std::hash::{BuildHasherDefault, Hash, Hasher};
-use style_traits::dom::ElementState;
-
-/// A hasher implementation that doesn't hash anything, because it expects its
-/// input to be a suitable u32 hash.
-pub struct PrecomputedHasher {
- hash: Option<u32>,
-}
-
-impl Default for PrecomputedHasher {
- fn default() -> Self {
- Self { hash: None }
- }
-}
-
-/// This is a set of pseudo-classes that are both relatively-rare (they don't
-/// affect most elements by default) and likely or known to have global rules
-/// (in e.g., the UA sheets).
-///
-/// We can avoid selector-matching those global rules for all elements without
-/// these pseudo-class states.
-const RARE_PSEUDO_CLASS_STATES: ElementState = ElementState::from_bits_truncate(
- ElementState::FULLSCREEN.bits() |
- ElementState::VISITED_OR_UNVISITED.bits() |
- ElementState::URLTARGET.bits() |
- ElementState::INERT.bits() |
- ElementState::FOCUS.bits() |
- ElementState::FOCUSRING.bits() |
- ElementState::TOPMOST_MODAL.bits(),
-);
-
-/// A simple alias for a hashmap using PrecomputedHasher.
-pub type PrecomputedHashMap<K, V> = HashMap<K, V, BuildHasherDefault<PrecomputedHasher>>;
-
-/// A simple alias for a hashset using PrecomputedHasher.
-pub type PrecomputedHashSet<K> = HashSet<K, BuildHasherDefault<PrecomputedHasher>>;
-
-impl Hasher for PrecomputedHasher {
- #[inline]
- fn write(&mut self, _: &[u8]) {
- unreachable!(
- "Called into PrecomputedHasher with something that isn't \
- a u32"
- )
- }
-
- #[inline]
- fn write_u32(&mut self, i: u32) {
- debug_assert!(self.hash.is_none());
- self.hash = Some(i);
- }
-
- #[inline]
- fn finish(&self) -> u64 {
- self.hash.expect("PrecomputedHasher wasn't fed?") as u64
- }
-}
-
-/// A trait to abstract over a given selector map entry.
-pub trait SelectorMapEntry: Sized + Clone {
- /// Gets the selector we should use to index in the selector map.
- fn selector(&self) -> SelectorIter<SelectorImpl>;
-}
-
-/// Map element data to selector-providing objects for which the last simple
-/// selector starts with them.
-///
-/// e.g.,
-/// "p > img" would go into the set of selectors corresponding to the
-/// element "img"
-/// "a .foo .bar.baz" would go into the set of selectors corresponding to
-/// the class "bar"
-///
-/// Because we match selectors right-to-left (i.e., moving up the tree
-/// from an element), we need to compare the last simple selector in the
-/// selector with the element.
-///
-/// So, if an element has ID "id1" and classes "foo" and "bar", then all
-/// the rules it matches will have their last simple selector starting
-/// either with "#id1" or with ".foo" or with ".bar".
-///
-/// Hence, the union of the rules keyed on each of element's classes, ID,
-/// element name, etc. will contain the Selectors that actually match that
-/// element.
-///
-/// We use a 1-entry SmallVec to avoid a separate heap allocation in the case
-/// where we only have one entry, which is quite common. See measurements in:
-/// * https://bugzilla.mozilla.org/show_bug.cgi?id=1363789#c5
-/// * https://bugzilla.mozilla.org/show_bug.cgi?id=681755
-///
-/// TODO: Tune the initial capacity of the HashMap
-#[derive(Clone, Debug, MallocSizeOf)]
-pub struct SelectorMap<T: 'static> {
- /// Rules that have `:root` selectors.
- pub root: SmallVec<[T; 1]>,
- /// A hash from an ID to rules which contain that ID selector.
- pub id_hash: MaybeCaseInsensitiveHashMap<Atom, SmallVec<[T; 1]>>,
- /// A hash from a class name to rules which contain that class selector.
- pub class_hash: MaybeCaseInsensitiveHashMap<Atom, SmallVec<[T; 1]>>,
- /// A hash from local name to rules which contain that local name selector.
- pub local_name_hash: PrecomputedHashMap<LocalName, SmallVec<[T; 1]>>,
- /// A hash from attributes to rules which contain that attribute selector.
- pub attribute_hash: PrecomputedHashMap<LocalName, SmallVec<[T; 1]>>,
- /// A hash from namespace to rules which contain that namespace selector.
- pub namespace_hash: PrecomputedHashMap<Namespace, SmallVec<[T; 1]>>,
- /// Rules for pseudo-states that are rare but have global selectors.
- pub rare_pseudo_classes: SmallVec<[T; 1]>,
- /// All other rules.
- pub other: SmallVec<[T; 1]>,
- /// Whether we should bucket by attribute names.
- bucket_attributes: bool,
- /// The number of entries in this map.
- pub count: usize,
-}
-
-impl<T: 'static> Default for SelectorMap<T> {
- #[inline]
- fn default() -> Self {
- Self::new()
- }
-}
-
-impl<T> SelectorMap<T> {
- /// Trivially constructs an empty `SelectorMap`.
- pub fn new() -> Self {
- SelectorMap {
- root: SmallVec::new(),
- id_hash: MaybeCaseInsensitiveHashMap::new(),
- class_hash: MaybeCaseInsensitiveHashMap::new(),
- attribute_hash: HashMap::default(),
- local_name_hash: HashMap::default(),
- namespace_hash: HashMap::default(),
- rare_pseudo_classes: SmallVec::new(),
- other: SmallVec::new(),
- bucket_attributes: static_prefs::pref!("layout.css.bucket-attribute-names.enabled"),
- count: 0,
- }
- }
-
- /// Trivially constructs an empty `SelectorMap`, with attribute bucketing
- /// explicitly disabled.
- pub fn new_without_attribute_bucketing() -> Self {
- let mut ret = Self::new();
- ret.bucket_attributes = false;
- ret
- }
-
- /// Shrink the capacity of the map if needed.
- pub fn shrink_if_needed(&mut self) {
- self.id_hash.shrink_if_needed();
- self.class_hash.shrink_if_needed();
- self.attribute_hash.shrink_if_needed();
- self.local_name_hash.shrink_if_needed();
- self.namespace_hash.shrink_if_needed();
- }
-
- /// Clears the hashmap retaining storage.
- pub fn clear(&mut self) {
- self.root.clear();
- self.id_hash.clear();
- self.class_hash.clear();
- self.attribute_hash.clear();
- self.local_name_hash.clear();
- self.namespace_hash.clear();
- self.rare_pseudo_classes.clear();
- self.other.clear();
- self.count = 0;
- }
-
- /// Returns whether there are any entries in the map.
- pub fn is_empty(&self) -> bool {
- self.count == 0
- }
-
- /// Returns the number of entries.
- pub fn len(&self) -> usize {
- self.count
- }
-}
-
-impl SelectorMap<Rule> {
- /// Append to `rule_list` all Rules in `self` that match element.
- ///
- /// Extract matching rules as per element's ID, classes, tag name, etc..
- /// Sort the Rules at the end to maintain cascading order.
- pub fn get_all_matching_rules<E>(
- &self,
- element: E,
- rule_hash_target: E,
- matching_rules_list: &mut ApplicableDeclarationList,
- matching_context: &mut MatchingContext<E::Impl>,
- cascade_level: CascadeLevel,
- cascade_data: &CascadeData,
- stylist: &Stylist,
- ) where
- E: TElement,
- {
- if self.is_empty() {
- return;
- }
-
- let quirks_mode = matching_context.quirks_mode();
-
- if rule_hash_target.is_root() {
- SelectorMap::get_matching_rules(
- element,
- &self.root,
- matching_rules_list,
- matching_context,
- cascade_level,
- cascade_data,
- stylist,
- );
- }
-
- if let Some(id) = rule_hash_target.id() {
- if let Some(rules) = self.id_hash.get(id, quirks_mode) {
- SelectorMap::get_matching_rules(
- element,
- rules,
- matching_rules_list,
- matching_context,
- cascade_level,
- cascade_data,
- stylist,
- )
- }
- }
-
- rule_hash_target.each_class(|class| {
- if let Some(rules) = self.class_hash.get(&class, quirks_mode) {
- SelectorMap::get_matching_rules(
- element,
- rules,
- matching_rules_list,
- matching_context,
- cascade_level,
- cascade_data,
- stylist,
- )
- }
- });
-
- if self.bucket_attributes {
- rule_hash_target.each_attr_name(|name| {
- if let Some(rules) = self.attribute_hash.get(name) {
- SelectorMap::get_matching_rules(
- element,
- rules,
- matching_rules_list,
- matching_context,
- cascade_level,
- cascade_data,
- stylist,
- )
- }
- });
- }
-
- if let Some(rules) = self.local_name_hash.get(rule_hash_target.local_name()) {
- SelectorMap::get_matching_rules(
- element,
- rules,
- matching_rules_list,
- matching_context,
- cascade_level,
- cascade_data,
- stylist,
- )
- }
-
- if rule_hash_target
- .state()
- .intersects(RARE_PSEUDO_CLASS_STATES)
- {
- SelectorMap::get_matching_rules(
- element,
- &self.rare_pseudo_classes,
- matching_rules_list,
- matching_context,
- cascade_level,
- cascade_data,
- stylist,
- );
- }
-
- if let Some(rules) = self.namespace_hash.get(rule_hash_target.namespace()) {
- SelectorMap::get_matching_rules(
- element,
- rules,
- matching_rules_list,
- matching_context,
- cascade_level,
- cascade_data,
- stylist,
- )
- }
-
- SelectorMap::get_matching_rules(
- element,
- &self.other,
- matching_rules_list,
- matching_context,
- cascade_level,
- cascade_data,
- stylist,
- );
- }
-
- /// Adds rules in `rules` that match `element` to the `matching_rules` list.
- pub(crate) fn get_matching_rules<E>(
- element: E,
- rules: &[Rule],
- matching_rules: &mut ApplicableDeclarationList,
- matching_context: &mut MatchingContext<E::Impl>,
- cascade_level: CascadeLevel,
- cascade_data: &CascadeData,
- stylist: &Stylist,
- ) where
- E: TElement,
- {
- for rule in rules {
- if !matches_selector(
- &rule.selector,
- 0,
- Some(&rule.hashes),
- &element,
- matching_context,
- ) {
- continue;
- }
-
- if rule.container_condition_id != ContainerConditionId::none() {
- if !cascade_data.container_condition_matches(
- rule.container_condition_id,
- stylist,
- element,
- matching_context,
- ) {
- continue;
- }
- }
-
- matching_rules.push(rule.to_applicable_declaration_block(cascade_level, cascade_data));
- }
- }
-}
-
-impl<T: SelectorMapEntry> SelectorMap<T> {
- /// Inserts an entry into the correct bucket(s).
- pub fn insert(&mut self, entry: T, quirks_mode: QuirksMode) -> Result<(), AllocErr> {
- self.count += 1;
-
- // NOTE(emilio): It'd be nice for this to be a separate function, but
- // then the compiler can't reason about the lifetime dependency between
- // `entry` and `bucket`, and would force us to clone the rule in the
- // common path.
- macro_rules! insert_into_bucket {
- ($entry:ident, $bucket:expr) => {{
- let vec = match $bucket {
- Bucket::Root => &mut self.root,
- Bucket::ID(id) => self
- .id_hash
- .try_entry(id.clone(), quirks_mode)?
- .or_default(),
- Bucket::Class(class) => self
- .class_hash
- .try_entry(class.clone(), quirks_mode)?
- .or_default(),
- Bucket::Attribute { name, lower_name } |
- Bucket::LocalName { name, lower_name } => {
- // If the local name in the selector isn't lowercase,
- // insert it into the rule hash twice. This means that,
- // during lookup, we can always find the rules based on
- // the local name of the element, regardless of whether
- // it's an html element in an html document (in which
- // case we match against lower_name) or not (in which
- // case we match against name).
- //
- // In the case of a non-html-element-in-html-document
- // with a lowercase localname and a non-lowercase
- // selector, the rulehash lookup may produce superfluous
- // selectors, but the subsequent selector matching work
- // will filter them out.
- let is_attribute = matches!($bucket, Bucket::Attribute { .. });
- let hash = if is_attribute {
- &mut self.attribute_hash
- } else {
- &mut self.local_name_hash
- };
- if name != lower_name {
- hash.try_reserve(1)?;
- let vec = hash.entry(lower_name.clone()).or_default();
- vec.try_reserve(1)?;
- vec.push($entry.clone());
- }
- hash.try_reserve(1)?;
- hash.entry(name.clone()).or_default()
- },
- Bucket::Namespace(url) => {
- self.namespace_hash.try_reserve(1)?;
- self.namespace_hash.entry(url.clone()).or_default()
- },
- Bucket::RarePseudoClasses => &mut self.rare_pseudo_classes,
- Bucket::Universal => &mut self.other,
- };
- vec.try_reserve(1)?;
- vec.push($entry);
- }};
- }
-
- let bucket = {
- let mut disjoint_buckets = SmallVec::new();
- let bucket = find_bucket(
- entry.selector(),
- &mut disjoint_buckets,
- self.bucket_attributes,
- );
-
- // See if inserting this selector in multiple entries in the
- // selector map would be worth it. Consider a case like:
- //
- // .foo:where(div, #bar)
- //
- // There, `bucket` would be `Class(foo)`, and disjoint_buckets would
- // be `[LocalName { div }, ID(bar)]`.
- //
- // Here we choose to insert the selector in the `.foo` bucket in
- // such a case, as it's likely more worth it than inserting it in
- // both `div` and `#bar`.
- //
- // This is specially true if there's any universal selector in the
- // `disjoint_selectors` set, at which point we'd just be doing
- // wasted work.
- if !disjoint_buckets.is_empty() &&
- disjoint_buckets
- .iter()
- .all(|b| b.more_specific_than(&bucket))
- {
- for bucket in &disjoint_buckets {
- let entry = entry.clone();
- insert_into_bucket!(entry, *bucket);
- }
- return Ok(());
- }
- bucket
- };
-
- insert_into_bucket!(entry, bucket);
- Ok(())
- }
-
- /// Looks up entries by id, class, local name, namespace, and other (in
- /// order).
- ///
- /// Each entry is passed to the callback, which returns true to continue
- /// iterating entries, or false to terminate the lookup.
- ///
- /// Returns false if the callback ever returns false.
- ///
- /// FIXME(bholley) This overlaps with SelectorMap<Rule>::get_all_matching_rules,
- /// but that function is extremely hot and I'd rather not rearrange it.
- pub fn lookup<'a, E, F>(&'a self, element: E, quirks_mode: QuirksMode, f: F) -> bool
- where
- E: TElement,
- F: FnMut(&'a T) -> bool,
- {
- self.lookup_with_state(element, element.state(), quirks_mode, f)
- }
-
- #[inline]
- fn lookup_with_state<'a, E, F>(
- &'a self,
- element: E,
- element_state: ElementState,
- quirks_mode: QuirksMode,
- mut f: F,
- ) -> bool
- where
- E: TElement,
- F: FnMut(&'a T) -> bool,
- {
- if element.is_root() {
- for entry in self.root.iter() {
- if !f(&entry) {
- return false;
- }
- }
- }
-
- if let Some(id) = element.id() {
- if let Some(v) = self.id_hash.get(id, quirks_mode) {
- for entry in v.iter() {
- if !f(&entry) {
- return false;
- }
- }
- }
- }
-
- let mut done = false;
- element.each_class(|class| {
- if done {
- return;
- }
- if let Some(v) = self.class_hash.get(class, quirks_mode) {
- for entry in v.iter() {
- if !f(&entry) {
- done = true;
- return;
- }
- }
- }
- });
-
- if done {
- return false;
- }
-
- if self.bucket_attributes {
- element.each_attr_name(|name| {
- if done {
- return;
- }
- if let Some(v) = self.attribute_hash.get(name) {
- for entry in v.iter() {
- if !f(&entry) {
- done = true;
- return;
- }
- }
- }
- });
-
- if done {
- return false;
- }
- }
-
- if let Some(v) = self.local_name_hash.get(element.local_name()) {
- for entry in v.iter() {
- if !f(&entry) {
- return false;
- }
- }
- }
-
- if let Some(v) = self.namespace_hash.get(element.namespace()) {
- for entry in v.iter() {
- if !f(&entry) {
- return false;
- }
- }
- }
-
- if element_state.intersects(RARE_PSEUDO_CLASS_STATES) {
- for entry in self.rare_pseudo_classes.iter() {
- if !f(&entry) {
- return false;
- }
- }
- }
-
- for entry in self.other.iter() {
- if !f(&entry) {
- return false;
- }
- }
-
- true
- }
-
- /// Performs a normal lookup, and also looks up entries for the passed-in
- /// id and classes.
- ///
- /// Each entry is passed to the callback, which returns true to continue
- /// iterating entries, or false to terminate the lookup.
- ///
- /// Returns false if the callback ever returns false.
- #[inline]
- pub fn lookup_with_additional<'a, E, F>(
- &'a self,
- element: E,
- quirks_mode: QuirksMode,
- additional_id: Option<&WeakAtom>,
- additional_classes: &[Atom],
- additional_states: ElementState,
- mut f: F,
- ) -> bool
- where
- E: TElement,
- F: FnMut(&'a T) -> bool,
- {
- // Do the normal lookup.
- if !self.lookup_with_state(
- element,
- element.state() | additional_states,
- quirks_mode,
- |entry| f(entry),
- ) {
- return false;
- }
-
- // Check the additional id.
- if let Some(id) = additional_id {
- if let Some(v) = self.id_hash.get(id, quirks_mode) {
- for entry in v.iter() {
- if !f(&entry) {
- return false;
- }
- }
- }
- }
-
- // Check the additional classes.
- for class in additional_classes {
- if let Some(v) = self.class_hash.get(class, quirks_mode) {
- for entry in v.iter() {
- if !f(&entry) {
- return false;
- }
- }
- }
- }
-
- true
- }
-}
-
-enum Bucket<'a> {
- Universal,
- Namespace(&'a Namespace),
- RarePseudoClasses,
- LocalName {
- name: &'a LocalName,
- lower_name: &'a LocalName,
- },
- Attribute {
- name: &'a LocalName,
- lower_name: &'a LocalName,
- },
- Class(&'a Atom),
- ID(&'a Atom),
- Root,
-}
-
-impl<'a> Bucket<'a> {
- /// root > id > class > local name > namespace > pseudo-classes > universal.
- #[inline]
- fn specificity(&self) -> usize {
- match *self {
- Bucket::Universal => 0,
- Bucket::Namespace(..) => 1,
- Bucket::RarePseudoClasses => 2,
- Bucket::LocalName { .. } => 3,
- Bucket::Attribute { .. } => 4,
- Bucket::Class(..) => 5,
- Bucket::ID(..) => 6,
- Bucket::Root => 7,
- }
- }
-
- #[inline]
- fn more_or_equally_specific_than(&self, other: &Self) -> bool {
- self.specificity() >= other.specificity()
- }
-
- #[inline]
- fn more_specific_than(&self, other: &Self) -> bool {
- self.specificity() > other.specificity()
- }
-}
-
-type DisjointBuckets<'a> = SmallVec<[Bucket<'a>; 5]>;
-
-fn specific_bucket_for<'a>(
- component: &'a Component<SelectorImpl>,
- disjoint_buckets: &mut DisjointBuckets<'a>,
- bucket_attributes: bool,
-) -> Bucket<'a> {
- match *component {
- Component::Root => Bucket::Root,
- Component::ID(ref id) => Bucket::ID(id),
- Component::Class(ref class) => Bucket::Class(class),
- Component::AttributeInNoNamespace { ref local_name, .. } if bucket_attributes => {
- Bucket::Attribute {
- name: local_name,
- lower_name: local_name,
- }
- },
- Component::AttributeInNoNamespaceExists {
- ref local_name,
- ref local_name_lower,
- } if bucket_attributes => Bucket::Attribute {
- name: local_name,
- lower_name: local_name_lower,
- },
- Component::AttributeOther(ref selector) if bucket_attributes => Bucket::Attribute {
- name: &selector.local_name,
- lower_name: &selector.local_name_lower,
- },
- Component::LocalName(ref selector) => Bucket::LocalName {
- name: &selector.name,
- lower_name: &selector.lower_name,
- },
- Component::Namespace(_, ref url) | Component::DefaultNamespace(ref url) => {
- Bucket::Namespace(url)
- },
- // ::slotted(..) isn't a normal pseudo-element, so we can insert it on
- // the rule hash normally without much problem. For example, in a
- // selector like:
- //
- // div::slotted(span)::before
- //
- // It looks like:
- //
- // [
- // LocalName(div),
- // Combinator(SlotAssignment),
- // Slotted(span),
- // Combinator::PseudoElement,
- // PseudoElement(::before),
- // ]
- //
- // So inserting `span` in the rule hash makes sense since we want to
- // match the slotted <span>.
- Component::Slotted(ref selector) => {
- find_bucket(selector.iter(), disjoint_buckets, bucket_attributes)
- },
- Component::Host(Some(ref selector)) => {
- find_bucket(selector.iter(), disjoint_buckets, bucket_attributes)
- },
- Component::Is(ref list) | Component::Where(ref list) => {
- if list.len() == 1 {
- find_bucket(list[0].iter(), disjoint_buckets, bucket_attributes)
- } else {
- for selector in &**list {
- let bucket = find_bucket(selector.iter(), disjoint_buckets, bucket_attributes);
- disjoint_buckets.push(bucket);
- }
- Bucket::Universal
- }
- },
- Component::NonTSPseudoClass(ref pseudo_class)
- if pseudo_class
- .state_flag()
- .intersects(RARE_PSEUDO_CLASS_STATES) =>
- {
- Bucket::RarePseudoClasses
- },
- _ => Bucket::Universal,
- }
-}
-
-/// Searches a compound selector from left to right, and returns the appropriate
-/// bucket for it.
-///
-/// It also populates disjoint_buckets with dependencies from nested selectors
-/// with any semantics like :is() and :where().
-#[inline(always)]
-fn find_bucket<'a>(
- mut iter: SelectorIter<'a, SelectorImpl>,
- disjoint_buckets: &mut DisjointBuckets<'a>,
- bucket_attributes: bool,
-) -> Bucket<'a> {
- let mut current_bucket = Bucket::Universal;
-
- loop {
- for ss in &mut iter {
- let new_bucket = specific_bucket_for(ss, disjoint_buckets, bucket_attributes);
- // NOTE: When presented with the choice of multiple specific selectors, use the
- // rightmost, on the assumption that that's less common, see bug 1829540.
- if new_bucket.more_or_equally_specific_than(&current_bucket) {
- current_bucket = new_bucket;
- }
- }
-
- // Effectively, pseudo-elements are ignored, given only state
- // pseudo-classes may appear before them.
- if iter.next_sequence() != Some(Combinator::PseudoElement) {
- break;
- }
- }
-
- current_bucket
-}
-
-/// Wrapper for PrecomputedHashMap that does ASCII-case-insensitive lookup in quirks mode.
-#[derive(Clone, Debug, MallocSizeOf)]
-pub struct MaybeCaseInsensitiveHashMap<K: PrecomputedHash + Hash + Eq, V>(PrecomputedHashMap<K, V>);
-
-impl<V> Default for MaybeCaseInsensitiveHashMap<Atom, V> {
- #[inline]
- fn default() -> Self {
- MaybeCaseInsensitiveHashMap(PrecomputedHashMap::default())
- }
-}
-
-impl<V> MaybeCaseInsensitiveHashMap<Atom, V> {
- /// Empty map
- pub fn new() -> Self {
- Self::default()
- }
-
- /// Shrink the capacity of the map if needed.
- pub fn shrink_if_needed(&mut self) {
- self.0.shrink_if_needed()
- }
-
- /// HashMap::try_entry
- pub fn try_entry(
- &mut self,
- mut key: Atom,
- quirks_mode: QuirksMode,
- ) -> Result<hash_map::Entry<Atom, V>, AllocErr> {
- if quirks_mode == QuirksMode::Quirks {
- key = key.to_ascii_lowercase()
- }
- self.0.try_reserve(1)?;
- Ok(self.0.entry(key))
- }
-
- /// HashMap::is_empty
- #[inline]
- pub fn is_empty(&self) -> bool {
- self.0.is_empty()
- }
-
- /// HashMap::iter
- pub fn iter(&self) -> hash_map::Iter<Atom, V> {
- self.0.iter()
- }
-
- /// HashMap::clear
- pub fn clear(&mut self) {
- self.0.clear()
- }
-
- /// HashMap::get
- pub fn get(&self, key: &WeakAtom, quirks_mode: QuirksMode) -> Option<&V> {
- if quirks_mode == QuirksMode::Quirks {
- self.0.get(&key.to_ascii_lowercase())
- } else {
- self.0.get(key)
- }
- }
-}
diff --git a/components/style/selector_parser.rs b/components/style/selector_parser.rs
deleted file mode 100644
index baf9ce83e61..00000000000
--- a/components/style/selector_parser.rs
+++ /dev/null
@@ -1,240 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
-
-//! The pseudo-classes and pseudo-elements supported by the style system.
-
-#![deny(missing_docs)]
-
-use crate::stylesheets::{Namespaces, Origin, UrlExtraData};
-use crate::values::serialize_atom_identifier;
-use crate::Atom;
-use cssparser::{Parser as CssParser, ParserInput};
-use selectors::parser::SelectorList;
-use std::fmt::{self, Debug, Write};
-use style_traits::{CssWriter, ParseError, ToCss};
-use style_traits::dom::ElementState;
-
-/// A convenient alias for the type that represents an attribute value used for
-/// selector parser implementation.
-pub type AttrValue = <SelectorImpl as ::selectors::SelectorImpl>::AttrValue;
-
-#[cfg(feature = "servo")]
-pub use crate::servo::selector_parser::*;
-
-#[cfg(feature = "gecko")]
-pub use crate::gecko::selector_parser::*;
-
-#[cfg(feature = "servo")]
-pub use crate::servo::selector_parser::ServoElementSnapshot as Snapshot;
-
-#[cfg(feature = "gecko")]
-pub use crate::gecko::snapshot::GeckoElementSnapshot as Snapshot;
-
-#[cfg(feature = "servo")]
-pub use crate::servo::restyle_damage::ServoRestyleDamage as RestyleDamage;
-
-#[cfg(feature = "gecko")]
-pub use crate::gecko::restyle_damage::GeckoRestyleDamage as RestyleDamage;
-
-/// Servo's selector parser.
-#[cfg_attr(feature = "servo", derive(MallocSizeOf))]
-pub struct SelectorParser<'a> {
- /// The origin of the stylesheet we're parsing.
- pub stylesheet_origin: Origin,
- /// The namespace set of the stylesheet.
- pub namespaces: &'a Namespaces,
- /// The extra URL data of the stylesheet, which is used to look up
- /// whether we are parsing a chrome:// URL style sheet.
- pub url_data: &'a UrlExtraData,
- /// Whether we're parsing selectors for `@supports`
- pub for_supports_rule: bool,
-}
-
-impl<'a> SelectorParser<'a> {
- /// Parse a selector list with an author origin and without taking into
- /// account namespaces.
- ///
- /// This is used for some DOM APIs like `querySelector`.
- pub fn parse_author_origin_no_namespace<'i>(
- input: &'i str,
- url_data: &UrlExtraData,
- ) -> Result<SelectorList<SelectorImpl>, ParseError<'i>> {
- let namespaces = Namespaces::default();
- let parser = SelectorParser {
- stylesheet_origin: Origin::Author,
- namespaces: &namespaces,
- url_data,
- for_supports_rule: false,
- };
- let mut input = ParserInput::new(input);
- SelectorList::parse(&parser, &mut CssParser::new(&mut input))
- }
-
- /// Whether we're parsing selectors in a user-agent stylesheet.
- pub fn in_user_agent_stylesheet(&self) -> bool {
- matches!(self.stylesheet_origin, Origin::UserAgent)
- }
-
- /// Whether we're parsing selectors in a stylesheet that has chrome
- /// privilege.
- pub fn chrome_rules_enabled(&self) -> bool {
- self.url_data.chrome_rules_enabled() || self.stylesheet_origin == Origin::User
- }
-}
-
-/// This enumeration determines if a pseudo-element is eagerly cascaded or not.
-///
-/// If you're implementing a public selector for `Servo` that the end-user might
-/// customize, then you probably need to make it eager.
-#[derive(Clone, Debug, Eq, PartialEq)]
-pub enum PseudoElementCascadeType {
- /// Eagerly cascaded pseudo-elements are "normal" pseudo-elements (i.e.
- /// `::before` and `::after`). They inherit styles normally as another
- /// selector would do, and they're computed as part of the cascade.
- Eager,
- /// Lazy pseudo-elements are affected by selector matching, but they're only
- /// computed when needed, and not before. They're useful for general
- /// pseudo-elements that are not very common.
- ///
- /// Note that in Servo lazy pseudo-elements are restricted to a subset of
- /// selectors, so you can't use it for public pseudo-elements. This is not
- /// the case with Gecko though.
- Lazy,
- /// Precomputed pseudo-elements skip the cascade process entirely, mostly as
- /// an optimisation since they are private pseudo-elements (like
- /// `::-servo-details-content`).
- ///
- /// This pseudo-elements are resolved on the fly using *only* global rules
- /// (rules of the form `*|*`), and applying them to the parent style.
- Precomputed,
-}
-
-/// A per-pseudo map, from a given pseudo to a `T`.
-#[derive(Clone, MallocSizeOf)]
-pub struct PerPseudoElementMap<T> {
- entries: [Option<T>; PSEUDO_COUNT],
-}
-
-impl<T> Default for PerPseudoElementMap<T> {
- fn default() -> Self {
- Self {
- entries: PseudoElement::pseudo_none_array(),
- }
- }
-}
-
-impl<T> Debug for PerPseudoElementMap<T>
-where
- T: Debug,
-{
- fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
- f.write_char('[')?;
- let mut first = true;
- for entry in self.entries.iter() {
- if !first {
- f.write_str(", ")?;
- }
- first = false;
- entry.fmt(f)?;
- }
- f.write_char(']')
- }
-}
-
-impl<T> PerPseudoElementMap<T> {
- /// Get an entry in the map.
- pub fn get(&self, pseudo: &PseudoElement) -> Option<&T> {
- self.entries[pseudo.index()].as_ref()
- }
-
- /// Clear this enumerated array.
- pub fn clear(&mut self) {
- *self = Self::default();
- }
-
- /// Set an entry value.
- ///
- /// Returns an error if the element is not a simple pseudo.
- pub fn set(&mut self, pseudo: &PseudoElement, value: T) {
- self.entries[pseudo.index()] = Some(value);
- }
-
- /// Get an entry for `pseudo`, or create it with calling `f`.
- pub fn get_or_insert_with<F>(&mut self, pseudo: &PseudoElement, f: F) -> &mut T
- where
- F: FnOnce() -> T,
- {
- let index = pseudo.index();
- if self.entries[index].is_none() {
- self.entries[index] = Some(f());
- }
- self.entries[index].as_mut().unwrap()
- }
-
- /// Get an iterator for the entries.
- pub fn iter(&self) -> std::slice::Iter<Option<T>> {
- self.entries.iter()
- }
-
- /// Get a mutable iterator for the entries.
- pub fn iter_mut(&mut self) -> std::slice::IterMut<Option<T>> {
- self.entries.iter_mut()
- }
-}
-
-/// Values for the :dir() pseudo class
-///
-/// "ltr" and "rtl" values are normalized to lowercase.
-#[derive(Clone, Debug, Eq, MallocSizeOf, PartialEq, ToShmem)]
-pub struct Direction(pub Atom);
-
-/// Horizontal values for the :dir() pseudo class
-#[derive(Clone, Debug, Eq, PartialEq)]
-pub enum HorizontalDirection {
- /// :dir(ltr)
- Ltr,
- /// :dir(rtl)
- Rtl,
-}
-
-impl Direction {
- /// Parse a direction value.
- pub fn parse<'i, 't>(parser: &mut CssParser<'i, 't>) -> Result<Self, ParseError<'i>> {
- let ident = parser.expect_ident()?;
- Ok(Direction(match_ignore_ascii_case! { &ident,
- "rtl" => atom!("rtl"),
- "ltr" => atom!("ltr"),
- _ => Atom::from(ident.as_ref()),
- }))
- }
-
- /// Convert this Direction into a HorizontalDirection, if applicable
- pub fn as_horizontal_direction(&self) -> Option<HorizontalDirection> {
- if self.0 == atom!("ltr") {
- Some(HorizontalDirection::Ltr)
- } else if self.0 == atom!("rtl") {
- Some(HorizontalDirection::Rtl)
- } else {
- None
- }
- }
-
- /// Gets the element state relevant to this :dir() selector.
- pub fn element_state(&self) -> ElementState {
- match self.as_horizontal_direction() {
- Some(HorizontalDirection::Ltr) => ElementState::LTR,
- Some(HorizontalDirection::Rtl) => ElementState::RTL,
- None => ElementState::empty(),
- }
- }
-}
-
-impl ToCss for Direction {
- fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
- where
- W: Write,
- {
- serialize_atom_identifier(&self.0, dest)
- }
-}
diff --git a/components/style/servo/media_queries.rs b/components/style/servo/media_queries.rs
deleted file mode 100644
index 7ee7e1cfe5f..00000000000
--- a/components/style/servo/media_queries.rs
+++ /dev/null
@@ -1,305 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
-
-//! Servo's media-query device and expression representation.
-
-use crate::color::AbsoluteColor;
-use crate::context::QuirksMode;
-use crate::custom_properties::CssEnvironment;
-use crate::font_metrics::FontMetrics;
-use crate::queries::feature::{AllowsRanges, Evaluator, FeatureFlags, QueryFeatureDescription};
-use crate::media_queries::MediaType;
-use crate::properties::ComputedValues;
-use crate::values::computed::CSSPixelLength;
-use crate::values::computed::Context;
-use crate::values::computed::Resolution;
-use crate::values::specified::font::FONT_MEDIUM_PX;
-use crate::values::specified::ViewportVariant;
-use crate::values::KeyframesName;
-use app_units::{Au, AU_PER_PX};
-use euclid::default::Size2D as UntypedSize2D;
-use euclid::{Scale, SideOffsets2D, Size2D};
-use mime::Mime;
-use std::sync::atomic::{AtomicBool, AtomicU32, Ordering};
-use style_traits::{CSSPixel, DevicePixel};
-
-/// A device is a structure that represents the current media a given document
-/// is displayed in.
-///
-/// This is the struct against which media queries are evaluated.
-#[derive(Debug, MallocSizeOf)]
-pub struct Device {
- /// The current media type used by de device.
- media_type: MediaType,
- /// The current viewport size, in CSS pixels.
- viewport_size: Size2D<f32, CSSPixel>,
- /// The current device pixel ratio, from CSS pixels to device pixels.
- device_pixel_ratio: Scale<f32, CSSPixel, DevicePixel>,
- /// The current quirks mode.
- #[ignore_malloc_size_of = "Pure stack type"]
- quirks_mode: QuirksMode,
-
- /// The font size of the root element
- /// This is set when computing the style of the root
- /// element, and used for rem units in other elements
- ///
- /// When computing the style of the root element, there can't be any
- /// other style being computed at the same time, given we need the style of
- /// the parent to compute everything else. So it is correct to just use
- /// a relaxed atomic here.
- #[ignore_malloc_size_of = "Pure stack type"]
- root_font_size: AtomicU32,
- /// Whether any styles computed in the document relied on the root font-size
- /// by using rem units.
- #[ignore_malloc_size_of = "Pure stack type"]
- used_root_font_size: AtomicBool,
- /// Whether any styles computed in the document relied on the viewport size.
- #[ignore_malloc_size_of = "Pure stack type"]
- used_viewport_units: AtomicBool,
- /// The CssEnvironment object responsible of getting CSS environment
- /// variables.
- environment: CssEnvironment,
-}
-
-impl Device {
- /// Trivially construct a new `Device`.
- pub fn new(
- media_type: MediaType,
- quirks_mode: QuirksMode,
- viewport_size: Size2D<f32, CSSPixel>,
- device_pixel_ratio: Scale<f32, CSSPixel, DevicePixel>,
- ) -> Device {
- Device {
- media_type,
- viewport_size,
- device_pixel_ratio,
- quirks_mode,
- // FIXME(bz): Seems dubious?
- root_font_size: AtomicU32::new(FONT_MEDIUM_PX.to_bits()),
- used_root_font_size: AtomicBool::new(false),
- used_viewport_units: AtomicBool::new(false),
- environment: CssEnvironment,
- }
- }
-
- /// Get the relevant environment to resolve `env()` functions.
- #[inline]
- pub fn environment(&self) -> &CssEnvironment {
- &self.environment
- }
-
- /// Return the default computed values for this device.
- pub fn default_computed_values(&self) -> &ComputedValues {
- // FIXME(bz): This isn't really right, but it's no more wrong
- // than what we used to do. See
- // https://github.com/servo/servo/issues/14773 for fixing it properly.
- ComputedValues::initial_values()
- }
-
- /// Get the font size of the root element (for rem)
- pub fn root_font_size(&self) -> CSSPixelLength {
- self.used_root_font_size.store(true, Ordering::Relaxed);
- CSSPixelLength::new(f32::from_bits(self.root_font_size.load(Ordering::Relaxed)))
- }
-
- /// Set the font size of the root element (for rem)
- pub fn set_root_font_size(&self, size: CSSPixelLength) {
- self.root_font_size
- .store(size.px().to_bits(), Ordering::Relaxed)
- }
-
- /// Get the quirks mode of the current device.
- pub fn quirks_mode(&self) -> QuirksMode {
- self.quirks_mode
- }
-
- /// Sets the body text color for the "inherit color from body" quirk.
- ///
- /// <https://quirks.spec.whatwg.org/#the-tables-inherit-color-from-body-quirk>
- pub fn set_body_text_color(&self, _color: AbsoluteColor) {
- // Servo doesn't implement this quirk (yet)
- }
-
- /// Whether a given animation name may be referenced from style.
- pub fn animation_name_may_be_referenced(&self, _: &KeyframesName) -> bool {
- // Assume it is, since we don't have any good way to prove it's not.
- true
- }
-
- /// Returns whether we ever looked up the root font size of the Device.
- pub fn used_root_font_size(&self) -> bool {
- self.used_root_font_size.load(Ordering::Relaxed)
- }
-
- /// Returns the viewport size of the current device in app units, needed,
- /// among other things, to resolve viewport units.
- #[inline]
- pub fn au_viewport_size(&self) -> UntypedSize2D<Au> {
- Size2D::new(
- Au::from_f32_px(self.viewport_size.width),
- Au::from_f32_px(self.viewport_size.height),
- )
- }
-
- /// Like the above, but records that we've used viewport units.
- pub fn au_viewport_size_for_viewport_unit_resolution(
- &self,
- _: ViewportVariant,
- ) -> UntypedSize2D<Au> {
- self.used_viewport_units.store(true, Ordering::Relaxed);
- // Servo doesn't have dynamic UA interfaces that affect the viewport,
- // so we can just ignore the ViewportVariant.
- self.au_viewport_size()
- }
-
- /// Whether viewport units were used since the last device change.
- pub fn used_viewport_units(&self) -> bool {
- self.used_viewport_units.load(Ordering::Relaxed)
- }
-
- /// Returns the number of app units per device pixel we're using currently.
- pub fn app_units_per_device_pixel(&self) -> i32 {
- (AU_PER_PX as f32 / self.device_pixel_ratio.0) as i32
- }
-
- /// Returns the device pixel ratio.
- pub fn device_pixel_ratio(&self) -> Scale<f32, CSSPixel, DevicePixel> {
- self.device_pixel_ratio
- }
-
- /// Gets the size of the scrollbar in CSS pixels.
- pub fn scrollbar_inline_size(&self) -> CSSPixelLength {
- // TODO: implement this.
- CSSPixelLength::new(0.0)
- }
-
- /// Queries dummy font metrics for Servo. Knows nothing about fonts and does not provide
- /// any metrics.
- /// TODO: Servo's font metrics provider will probably not live in this crate, so this will
- /// have to be replaced with something else (perhaps a trait method on TElement)
- /// when we get there
- pub fn query_font_metrics(
- &self,
- _vertical: bool,
- _font: &crate::properties::style_structs::Font,
- _base_size: CSSPixelLength,
- _in_media_query: bool,
- _retrieve_math_scales: bool,
- ) -> FontMetrics {
- Default::default()
- }
-
- /// Return the media type of the current device.
- pub fn media_type(&self) -> MediaType {
- self.media_type.clone()
- }
-
- /// Returns whether document colors are enabled.
- pub fn use_document_colors(&self) -> bool {
- true
- }
-
- /// Returns the default background color.
- pub fn default_background_color(&self) -> AbsoluteColor {
- AbsoluteColor::white()
- }
-
- /// Returns the default foreground color.
- pub fn default_color(&self) -> AbsoluteColor {
- AbsoluteColor::black()
- }
-
- /// Returns safe area insets
- pub fn safe_area_insets(&self) -> SideOffsets2D<f32, CSSPixel> {
- SideOffsets2D::zero()
- }
-
- /// Returns true if the given MIME type is supported
- pub fn is_supported_mime_type(&self, mime_type: &str) -> bool {
- match mime_type.parse::<Mime>() {
- Ok(m) => {
- // Keep this in sync with 'image_classifer' from
- // components/net/mime_classifier.rs
- m == mime::IMAGE_BMP
- || m == mime::IMAGE_GIF
- || m == mime::IMAGE_PNG
- || m == mime::IMAGE_JPEG
- || m == "image/x-icon"
- || m == "image/webp"
- }
- _ => false,
- }
- }
-
- /// Return whether the document is a chrome document.
- #[inline]
- pub fn chrome_rules_enabled_for_document(&self) -> bool {
- false
- }
-}
-
-/// https://drafts.csswg.org/mediaqueries-4/#width
-fn eval_width(context: &Context) -> CSSPixelLength {
- CSSPixelLength::new(context.device().au_viewport_size().width.to_f32_px())
-}
-
-#[derive(Clone, Copy, Debug, FromPrimitive, Parse, ToCss)]
-#[repr(u8)]
-enum Scan {
- Progressive,
- Interlace,
-}
-
-/// https://drafts.csswg.org/mediaqueries-4/#scan
-fn eval_scan(_: &Context, _: Option<Scan>) -> bool {
- // Since we doesn't support the 'tv' media type, the 'scan' feature never
- // matches.
- false
-}
-
-/// https://drafts.csswg.org/mediaqueries-4/#resolution
-fn eval_resolution(context: &Context) -> Resolution {
- Resolution::from_dppx(context.device().device_pixel_ratio.0)
-}
-
-/// https://compat.spec.whatwg.org/#css-media-queries-webkit-device-pixel-ratio
-fn eval_device_pixel_ratio(context: &Context) -> f32 {
- eval_resolution(context).dppx()
-}
-
-lazy_static! {
- /// A list with all the media features that Servo supports.
- pub static ref MEDIA_FEATURES: [QueryFeatureDescription; 5] = [
- feature!(
- atom!("width"),
- AllowsRanges::Yes,
- Evaluator::Length(eval_width),
- FeatureFlags::empty(),
- ),
- feature!(
- atom!("scan"),
- AllowsRanges::No,
- keyword_evaluator!(eval_scan, Scan),
- FeatureFlags::empty(),
- ),
- feature!(
- atom!("resolution"),
- AllowsRanges::Yes,
- Evaluator::Resolution(eval_resolution),
- FeatureFlags::empty(),
- ),
- feature!(
- atom!("device-pixel-ratio"),
- AllowsRanges::Yes,
- Evaluator::Float(eval_device_pixel_ratio),
- FeatureFlags::WEBKIT_PREFIX,
- ),
- feature!(
- atom!("-moz-device-pixel-ratio"),
- AllowsRanges::Yes,
- Evaluator::Float(eval_device_pixel_ratio),
- FeatureFlags::empty(),
- ),
- ];
-}
diff --git a/components/style/servo/mod.rs b/components/style/servo/mod.rs
deleted file mode 100644
index 6502d287278..00000000000
--- a/components/style/servo/mod.rs
+++ /dev/null
@@ -1,12 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
-
-//! Servo-specific bits of the style system.
-//!
-//! These get compiled out on a Gecko build.
-
-pub mod media_queries;
-pub mod restyle_damage;
-pub mod selector_parser;
-pub mod url;
diff --git a/components/style/servo/restyle_damage.rs b/components/style/servo/restyle_damage.rs
deleted file mode 100644
index 628a1533a68..00000000000
--- a/components/style/servo/restyle_damage.rs
+++ /dev/null
@@ -1,269 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
-
-//! The restyle damage is a hint that tells layout which kind of operations may
-//! be needed in presence of incremental style changes.
-
-use crate::computed_values::display::T as Display;
-use crate::matching::{StyleChange, StyleDifference};
-use crate::properties::ComputedValues;
-use std::fmt;
-
-bitflags! {
- /// Individual layout actions that may be necessary after restyling.
- pub struct ServoRestyleDamage: u8 {
- /// Repaint the node itself.
- ///
- /// Currently unused; need to decide how this propagates.
- const REPAINT = 0x01;
-
- /// The stacking-context-relative position of this node or its
- /// descendants has changed.
- ///
- /// Propagates both up and down the flow tree.
- const REPOSITION = 0x02;
-
- /// Recompute the overflow regions (bounding box of object and all descendants).
- ///
- /// Propagates down the flow tree because the computation is bottom-up.
- const STORE_OVERFLOW = 0x04;
-
- /// Recompute intrinsic inline_sizes (minimum and preferred).
- ///
- /// Propagates down the flow tree because the computation is.
- /// bottom-up.
- const BUBBLE_ISIZES = 0x08;
-
- /// Recompute actual inline-sizes and block-sizes, only taking
- /// out-of-flow children into account.
- ///
- /// Propagates up the flow tree because the computation is top-down.
- const REFLOW_OUT_OF_FLOW = 0x10;
-
- /// Recompute actual inline_sizes and block_sizes.
- ///
- /// Propagates up the flow tree because the computation is top-down.
- const REFLOW = 0x20;
-
- /// Re-resolve generated content.
- ///
- /// Propagates up the flow tree because the computation is inorder.
- const RESOLVE_GENERATED_CONTENT = 0x40;
-
- /// The entire flow needs to be reconstructed.
- const RECONSTRUCT_FLOW = 0x80;
- }
-}
-
-malloc_size_of_is_0!(ServoRestyleDamage);
-
-impl ServoRestyleDamage {
- /// Compute the `StyleDifference` (including the appropriate restyle damage)
- /// for a given style change between `old` and `new`.
- pub fn compute_style_difference(old: &ComputedValues, new: &ComputedValues) -> StyleDifference {
- let damage = compute_damage(old, new);
- let change = if damage.is_empty() {
- StyleChange::Unchanged
- } else {
- // FIXME(emilio): Differentiate between reset and inherited
- // properties here, and set `reset_only` appropriately so the
- // optimization to skip the cascade in those cases applies.
- StyleChange::Changed { reset_only: false }
- };
- StyleDifference { damage, change }
- }
-
- /// Returns a bitmask that represents a flow that needs to be rebuilt and
- /// reflowed.
- ///
- /// FIXME(bholley): Do we ever actually need this? Shouldn't
- /// RECONSTRUCT_FLOW imply everything else?
- pub fn rebuild_and_reflow() -> ServoRestyleDamage {
- ServoRestyleDamage::REPAINT |
- ServoRestyleDamage::REPOSITION |
- ServoRestyleDamage::STORE_OVERFLOW |
- ServoRestyleDamage::BUBBLE_ISIZES |
- ServoRestyleDamage::REFLOW_OUT_OF_FLOW |
- ServoRestyleDamage::REFLOW |
- ServoRestyleDamage::RECONSTRUCT_FLOW
- }
-
- /// Returns a bitmask indicating that the frame needs to be reconstructed.
- pub fn reconstruct() -> ServoRestyleDamage {
- ServoRestyleDamage::RECONSTRUCT_FLOW
- }
-
- /// Supposing a flow has the given `position` property and this damage,
- /// returns the damage that we should add to the *parent* of this flow.
- pub fn damage_for_parent(self, child_is_absolutely_positioned: bool) -> ServoRestyleDamage {
- if child_is_absolutely_positioned {
- self & (ServoRestyleDamage::REPAINT |
- ServoRestyleDamage::REPOSITION |
- ServoRestyleDamage::STORE_OVERFLOW |
- ServoRestyleDamage::REFLOW_OUT_OF_FLOW |
- ServoRestyleDamage::RESOLVE_GENERATED_CONTENT)
- } else {
- self & (ServoRestyleDamage::REPAINT |
- ServoRestyleDamage::REPOSITION |
- ServoRestyleDamage::STORE_OVERFLOW |
- ServoRestyleDamage::REFLOW |
- ServoRestyleDamage::REFLOW_OUT_OF_FLOW |
- ServoRestyleDamage::RESOLVE_GENERATED_CONTENT)
- }
- }
-
- /// Supposing the *parent* of a flow with the given `position` property has
- /// this damage, returns the damage that we should add to this flow.
- pub fn damage_for_child(
- self,
- parent_is_absolutely_positioned: bool,
- child_is_absolutely_positioned: bool,
- ) -> ServoRestyleDamage {
- match (
- parent_is_absolutely_positioned,
- child_is_absolutely_positioned,
- ) {
- (false, true) => {
- // Absolute children are out-of-flow and therefore insulated from changes.
- //
- // FIXME(pcwalton): Au contraire, if the containing block dimensions change!
- self & (ServoRestyleDamage::REPAINT | ServoRestyleDamage::REPOSITION)
- },
- (true, false) => {
- // Changing the position of an absolutely-positioned block requires us to reflow
- // its kids.
- if self.contains(ServoRestyleDamage::REFLOW_OUT_OF_FLOW) {
- self | ServoRestyleDamage::REFLOW
- } else {
- self
- }
- },
- _ => {
- // TODO(pcwalton): Take floatedness into account.
- self & (ServoRestyleDamage::REPAINT |
- ServoRestyleDamage::REPOSITION |
- ServoRestyleDamage::REFLOW)
- },
- }
- }
-}
-
-impl Default for ServoRestyleDamage {
- fn default() -> Self {
- Self::empty()
- }
-}
-
-impl fmt::Display for ServoRestyleDamage {
- fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
- let mut first_elem = true;
-
- let to_iter = [
- (ServoRestyleDamage::REPAINT, "Repaint"),
- (ServoRestyleDamage::REPOSITION, "Reposition"),
- (ServoRestyleDamage::STORE_OVERFLOW, "StoreOverflow"),
- (ServoRestyleDamage::BUBBLE_ISIZES, "BubbleISizes"),
- (ServoRestyleDamage::REFLOW_OUT_OF_FLOW, "ReflowOutOfFlow"),
- (ServoRestyleDamage::REFLOW, "Reflow"),
- (
- ServoRestyleDamage::RESOLVE_GENERATED_CONTENT,
- "ResolveGeneratedContent",
- ),
- (ServoRestyleDamage::RECONSTRUCT_FLOW, "ReconstructFlow"),
- ];
-
- for &(damage, damage_str) in &to_iter {
- if self.contains(damage) {
- if !first_elem {
- write!(f, " | ")?;
- }
- write!(f, "{}", damage_str)?;
- first_elem = false;
- }
- }
-
- if first_elem {
- write!(f, "NoDamage")?;
- }
-
- Ok(())
- }
-}
-
-fn compute_damage(old: &ComputedValues, new: &ComputedValues) -> ServoRestyleDamage {
- let mut damage = ServoRestyleDamage::empty();
-
- // This should check every CSS property, as enumerated in the fields of
- // https://doc.servo.org/style/properties/struct.ComputedValues.html
-
- // This uses short-circuiting boolean OR for its side effects and ignores the result.
- let _ = restyle_damage_rebuild_and_reflow!(
- old,
- new,
- damage,
- [
- ServoRestyleDamage::REPAINT,
- ServoRestyleDamage::REPOSITION,
- ServoRestyleDamage::STORE_OVERFLOW,
- ServoRestyleDamage::BUBBLE_ISIZES,
- ServoRestyleDamage::REFLOW_OUT_OF_FLOW,
- ServoRestyleDamage::REFLOW,
- ServoRestyleDamage::RECONSTRUCT_FLOW
- ],
- old.get_box().original_display != new.get_box().original_display
- ) || (new.get_box().display == Display::Inline &&
- restyle_damage_rebuild_and_reflow_inline!(
- old,
- new,
- damage,
- [
- ServoRestyleDamage::REPAINT,
- ServoRestyleDamage::REPOSITION,
- ServoRestyleDamage::STORE_OVERFLOW,
- ServoRestyleDamage::BUBBLE_ISIZES,
- ServoRestyleDamage::REFLOW_OUT_OF_FLOW,
- ServoRestyleDamage::REFLOW,
- ServoRestyleDamage::RECONSTRUCT_FLOW
- ]
- )) ||
- restyle_damage_reflow!(
- old,
- new,
- damage,
- [
- ServoRestyleDamage::REPAINT,
- ServoRestyleDamage::REPOSITION,
- ServoRestyleDamage::STORE_OVERFLOW,
- ServoRestyleDamage::BUBBLE_ISIZES,
- ServoRestyleDamage::REFLOW_OUT_OF_FLOW,
- ServoRestyleDamage::REFLOW
- ]
- ) ||
- restyle_damage_reflow_out_of_flow!(
- old,
- new,
- damage,
- [
- ServoRestyleDamage::REPAINT,
- ServoRestyleDamage::REPOSITION,
- ServoRestyleDamage::STORE_OVERFLOW,
- ServoRestyleDamage::REFLOW_OUT_OF_FLOW
- ]
- ) ||
- restyle_damage_repaint!(old, new, damage, [ServoRestyleDamage::REPAINT]);
-
- // Paint worklets may depend on custom properties,
- // so if they have changed we should repaint.
- if !old.custom_properties_equal(new) {
- damage.insert(ServoRestyleDamage::REPAINT);
- }
-
- // If the layer requirements of this flow have changed due to the value
- // of the transform, then reflow is required to rebuild the layers.
- if old.transform_requires_layer() != new.transform_requires_layer() {
- damage.insert(ServoRestyleDamage::rebuild_and_reflow());
- }
-
- damage
-}
diff --git a/components/style/servo/selector_parser.rs b/components/style/servo/selector_parser.rs
deleted file mode 100644
index 0df3fe6b0ec..00000000000
--- a/components/style/servo/selector_parser.rs
+++ /dev/null
@@ -1,837 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
-
-#![deny(missing_docs)]
-
-//! Servo's selector parser.
-
-use crate::attr::{AttrIdentifier, AttrValue};
-use crate::computed_value_flags::ComputedValueFlags;
-use crate::dom::{OpaqueNode, TElement, TNode};
-use crate::invalidation::element::document_state::InvalidationMatchingData;
-use crate::invalidation::element::element_wrapper::ElementSnapshot;
-use crate::properties::longhands::display::computed_value::T as Display;
-use crate::properties::{ComputedValues, PropertyFlags};
-use crate::selector_parser::AttrValue as SelectorAttrValue;
-use crate::selector_parser::{PseudoElementCascadeType, SelectorParser};
-use crate::values::{AtomIdent, AtomString};
-use crate::{Atom, CaseSensitivityExt, LocalName, Namespace, Prefix};
-use cssparser::{serialize_identifier, CowRcStr, Parser as CssParser, SourceLocation, ToCss};
-use style_traits::dom::{DocumentState, ElementState};
-use fxhash::FxHashMap;
-use selectors::attr::{AttrSelectorOperation, CaseSensitivity, NamespaceConstraint};
-use selectors::parser::SelectorParseErrorKind;
-use selectors::visitor::SelectorVisitor;
-use std::fmt;
-use std::mem;
-use std::ops::{Deref, DerefMut};
-use style_traits::{ParseError, StyleParseErrorKind};
-
-/// A pseudo-element, both public and private.
-///
-/// NB: If you add to this list, be sure to update `each_simple_pseudo_element` too.
-#[derive(
- Clone, Copy, Debug, Deserialize, Eq, Hash, MallocSizeOf, PartialEq, Serialize, ToShmem,
-)]
-#[allow(missing_docs)]
-#[repr(usize)]
-pub enum PseudoElement {
- // Eager pseudos. Keep these first so that eager_index() works.
- After = 0,
- Before,
- Selection,
- // If/when :first-letter is added, update is_first_letter accordingly.
-
- // If/when :first-line is added, update is_first_line accordingly.
-
- // If/when ::first-letter, ::first-line, or ::placeholder are added, adjust
- // our property_restriction implementation to do property filtering for
- // them. Also, make sure the UA sheet has the !important rules some of the
- // APPLIES_TO_PLACEHOLDER properties expect!
-
- // Non-eager pseudos.
- DetailsSummary,
- DetailsContent,
- ServoAnonymousBox,
- ServoAnonymousTableCell,
- ServoAnonymousTableRow,
- ServoLegacyText,
- ServoLegacyInputText,
- ServoLegacyTableWrapper,
- ServoLegacyAnonymousTableWrapper,
- ServoLegacyAnonymousTable,
- ServoLegacyAnonymousBlock,
- ServoLegacyInlineBlockWrapper,
- ServoLegacyInlineAbsolute,
-}
-
-/// The count of all pseudo-elements.
-pub const PSEUDO_COUNT: usize = PseudoElement::ServoLegacyInlineAbsolute as usize + 1;
-
-impl ToCss for PseudoElement {
- fn to_css<W>(&self, dest: &mut W) -> fmt::Result
- where
- W: fmt::Write,
- {
- use self::PseudoElement::*;
- dest.write_str(match *self {
- After => "::after",
- Before => "::before",
- Selection => "::selection",
- DetailsSummary => "::-servo-details-summary",
- DetailsContent => "::-servo-details-content",
- ServoAnonymousBox => "::-servo-anonymous-box",
- ServoAnonymousTableCell => "::-servo-anonymous-table-cell",
- ServoAnonymousTableRow => "::-servo-anonymous-table-row",
- ServoLegacyText => "::-servo-legacy-text",
- ServoLegacyInputText => "::-servo-legacy-input-text",
- ServoLegacyTableWrapper => "::-servo-legacy-table-wrapper",
- ServoLegacyAnonymousTableWrapper => "::-servo-legacy-anonymous-table-wrapper",
- ServoLegacyAnonymousTable => "::-servo-legacy-anonymous-table",
- ServoLegacyAnonymousBlock => "::-servo-legacy-anonymous-block",
- ServoLegacyInlineBlockWrapper => "::-servo-legacy-inline-block-wrapper",
- ServoLegacyInlineAbsolute => "::-servo-legacy-inline-absolute",
- })
- }
-}
-
-impl ::selectors::parser::PseudoElement for PseudoElement {
- type Impl = SelectorImpl;
-}
-
-/// The number of eager pseudo-elements. Keep this in sync with cascade_type.
-pub const EAGER_PSEUDO_COUNT: usize = 3;
-
-impl PseudoElement {
- /// Gets the canonical index of this eagerly-cascaded pseudo-element.
- #[inline]
- pub fn eager_index(&self) -> usize {
- debug_assert!(self.is_eager());
- self.clone() as usize
- }
-
- /// An index for this pseudo-element to be indexed in an enumerated array.
- #[inline]
- pub fn index(&self) -> usize {
- self.clone() as usize
- }
-
- /// An array of `None`, one per pseudo-element.
- pub fn pseudo_none_array<T>() -> [Option<T>; PSEUDO_COUNT] {
- Default::default()
- }
-
- /// Creates a pseudo-element from an eager index.
- #[inline]
- pub fn from_eager_index(i: usize) -> Self {
- assert!(i < EAGER_PSEUDO_COUNT);
- let result: PseudoElement = unsafe { mem::transmute(i) };
- debug_assert!(result.is_eager());
- result
- }
-
- /// Whether the current pseudo element is ::before or ::after.
- #[inline]
- pub fn is_before_or_after(&self) -> bool {
- self.is_before() || self.is_after()
- }
-
- /// Whether this is an unknown ::-webkit- pseudo-element.
- #[inline]
- pub fn is_unknown_webkit_pseudo_element(&self) -> bool {
- false
- }
-
- /// Whether this pseudo-element is the ::marker pseudo.
- #[inline]
- pub fn is_marker(&self) -> bool {
- false
- }
-
- /// Whether this pseudo-element is the ::selection pseudo.
- #[inline]
- pub fn is_selection(&self) -> bool {
- *self == PseudoElement::Selection
- }
-
- /// Whether this pseudo-element is the ::before pseudo.
- #[inline]
- pub fn is_before(&self) -> bool {
- *self == PseudoElement::Before
- }
-
- /// Whether this pseudo-element is the ::after pseudo.
- #[inline]
- pub fn is_after(&self) -> bool {
- *self == PseudoElement::After
- }
-
- /// Whether the current pseudo element is :first-letter
- #[inline]
- pub fn is_first_letter(&self) -> bool {
- false
- }
-
- /// Whether the current pseudo element is :first-line
- #[inline]
- pub fn is_first_line(&self) -> bool {
- false
- }
-
- /// Whether this pseudo-element is the ::-moz-color-swatch pseudo.
- #[inline]
- pub fn is_color_swatch(&self) -> bool {
- false
- }
-
- /// Whether this pseudo-element is eagerly-cascaded.
- #[inline]
- pub fn is_eager(&self) -> bool {
- self.cascade_type() == PseudoElementCascadeType::Eager
- }
-
- /// Whether this pseudo-element is lazily-cascaded.
- #[inline]
- pub fn is_lazy(&self) -> bool {
- self.cascade_type() == PseudoElementCascadeType::Lazy
- }
-
- /// Whether this pseudo-element is for an anonymous box.
- pub fn is_anon_box(&self) -> bool {
- self.is_precomputed()
- }
-
- /// Whether this pseudo-element skips flex/grid container display-based
- /// fixup.
- #[inline]
- pub fn skip_item_display_fixup(&self) -> bool {
- !self.is_before_or_after()
- }
-
- /// Whether this pseudo-element is precomputed.
- #[inline]
- pub fn is_precomputed(&self) -> bool {
- self.cascade_type() == PseudoElementCascadeType::Precomputed
- }
-
- /// Returns which kind of cascade type has this pseudo.
- ///
- /// For more info on cascade types, see docs/components/style.md
- ///
- /// Note: Keep this in sync with EAGER_PSEUDO_COUNT.
- #[inline]
- pub fn cascade_type(&self) -> PseudoElementCascadeType {
- match *self {
- PseudoElement::After | PseudoElement::Before | PseudoElement::Selection => {
- PseudoElementCascadeType::Eager
- },
- PseudoElement::DetailsSummary => PseudoElementCascadeType::Lazy,
- PseudoElement::DetailsContent |
- PseudoElement::ServoAnonymousBox |
- PseudoElement::ServoAnonymousTableCell |
- PseudoElement::ServoAnonymousTableRow |
- PseudoElement::ServoLegacyText |
- PseudoElement::ServoLegacyInputText |
- PseudoElement::ServoLegacyTableWrapper |
- PseudoElement::ServoLegacyAnonymousTableWrapper |
- PseudoElement::ServoLegacyAnonymousTable |
- PseudoElement::ServoLegacyAnonymousBlock |
- PseudoElement::ServoLegacyInlineBlockWrapper |
- PseudoElement::ServoLegacyInlineAbsolute => PseudoElementCascadeType::Precomputed,
- }
- }
-
- /// Covert non-canonical pseudo-element to canonical one, and keep a
- /// canonical one as it is.
- pub fn canonical(&self) -> PseudoElement {
- self.clone()
- }
-
- /// Stub, only Gecko needs this
- pub fn pseudo_info(&self) {
- ()
- }
-
- /// Property flag that properties must have to apply to this pseudo-element.
- #[inline]
- pub fn property_restriction(&self) -> Option<PropertyFlags> {
- None
- }
-
- /// Whether this pseudo-element should actually exist if it has
- /// the given styles.
- pub fn should_exist(&self, style: &ComputedValues) -> bool {
- let display = style.get_box().clone_display();
- if display == Display::None {
- return false;
- }
- if self.is_before_or_after() && style.ineffective_content_property() {
- return false;
- }
-
- true
- }
-}
-
-/// The type used for storing `:lang` arguments.
-pub type Lang = Box<str>;
-
-/// A non tree-structural pseudo-class.
-/// See https://drafts.csswg.org/selectors-4/#structural-pseudos
-#[derive(Clone, Debug, Eq, Hash, MallocSizeOf, PartialEq, ToShmem)]
-#[allow(missing_docs)]
-pub enum NonTSPseudoClass {
- Active,
- AnyLink,
- Checked,
- Valid,
- Invalid,
- Defined,
- Disabled,
- Enabled,
- Focus,
- Fullscreen,
- Hover,
- Indeterminate,
- Lang(Lang),
- Link,
- PlaceholderShown,
- ReadWrite,
- ReadOnly,
- ServoNonZeroBorder,
- Target,
- Visited,
-}
-
-impl ::selectors::parser::NonTSPseudoClass for NonTSPseudoClass {
- type Impl = SelectorImpl;
-
- #[inline]
- fn is_active_or_hover(&self) -> bool {
- matches!(*self, NonTSPseudoClass::Active | NonTSPseudoClass::Hover)
- }
-
- #[inline]
- fn is_user_action_state(&self) -> bool {
- matches!(
- *self,
- NonTSPseudoClass::Active | NonTSPseudoClass::Hover | NonTSPseudoClass::Focus
- )
- }
-
- fn visit<V>(&self, _: &mut V) -> bool
- where
- V: SelectorVisitor<Impl = Self::Impl>,
- {
- true
- }
-}
-
-impl ToCss for NonTSPseudoClass {
- fn to_css<W>(&self, dest: &mut W) -> fmt::Result
- where
- W: fmt::Write,
- {
- use self::NonTSPseudoClass::*;
- if let Lang(ref lang) = *self {
- dest.write_str(":lang(")?;
- serialize_identifier(lang, dest)?;
- return dest.write_char(')');
- }
-
- dest.write_str(match *self {
- Active => ":active",
- AnyLink => ":any-link",
- Checked => ":checked",
- Valid => ":valid",
- Invalid => ":invalid",
- Defined => ":defined",
- Disabled => ":disabled",
- Enabled => ":enabled",
- Focus => ":focus",
- Fullscreen => ":fullscreen",
- Hover => ":hover",
- Indeterminate => ":indeterminate",
- Link => ":link",
- PlaceholderShown => ":placeholder-shown",
- ReadWrite => ":read-write",
- ReadOnly => ":read-only",
- ServoNonZeroBorder => ":-servo-nonzero-border",
- Target => ":target",
- Visited => ":visited",
- Lang(_) => unreachable!(),
- })
- }
-}
-
-impl NonTSPseudoClass {
- /// Gets a given state flag for this pseudo-class. This is used to do
- /// selector matching, and it's set from the DOM.
- pub fn state_flag(&self) -> ElementState {
- use self::NonTSPseudoClass::*;
- match *self {
- Active => ElementState::ACTIVE,
- Focus => ElementState::FOCUS,
- Fullscreen => ElementState::FULLSCREEN,
- Hover => ElementState::HOVER,
- Defined => ElementState::DEFINED,
- Enabled => ElementState::ENABLED,
- Disabled => ElementState::DISABLED,
- Checked => ElementState::CHECKED,
- Valid => ElementState::VALID,
- Invalid => ElementState::INVALID,
- Indeterminate => ElementState::INDETERMINATE,
- ReadOnly | ReadWrite => ElementState::READWRITE,
- PlaceholderShown => ElementState::PLACEHOLDER_SHOWN,
- Target => ElementState::URLTARGET,
-
- AnyLink | Lang(_) | Link | Visited | ServoNonZeroBorder => ElementState::empty(),
- }
- }
-
- /// Get the document state flag associated with a pseudo-class, if any.
- pub fn document_state_flag(&self) -> DocumentState {
- DocumentState::empty()
- }
-
- /// Returns true if the given pseudoclass should trigger style sharing cache revalidation.
- pub fn needs_cache_revalidation(&self) -> bool {
- self.state_flag().is_empty()
- }
-}
-
-/// The abstract struct we implement the selector parser implementation on top
-/// of.
-#[derive(Clone, Debug, PartialEq)]
-#[cfg_attr(feature = "servo", derive(MallocSizeOf))]
-pub struct SelectorImpl;
-
-/// A set of extra data to carry along with the matching context, either for
-/// selector-matching or invalidation.
-#[derive(Debug, Default)]
-pub struct ExtraMatchingData<'a> {
- /// The invalidation data to invalidate doc-state pseudo-classes correctly.
- pub invalidation_data: InvalidationMatchingData,
-
- /// The invalidation bits from matching container queries. These are here
- /// just for convenience mostly.
- pub cascade_input_flags: ComputedValueFlags,
-
- /// The style of the originating element in order to evaluate @container
- /// size queries affecting pseudo-elements.
- pub originating_element_style: Option<&'a ComputedValues>,
-}
-
-impl ::selectors::SelectorImpl for SelectorImpl {
- type PseudoElement = PseudoElement;
- type NonTSPseudoClass = NonTSPseudoClass;
-
- type ExtraMatchingData<'a> = ExtraMatchingData<'a>;
- type AttrValue = AtomString;
- type Identifier = AtomIdent;
- type LocalName = LocalName;
- type NamespacePrefix = Prefix;
- type NamespaceUrl = Namespace;
- type BorrowedLocalName = html5ever::LocalName;
- type BorrowedNamespaceUrl = html5ever::Namespace;
-}
-
-impl<'a, 'i> ::selectors::Parser<'i> for SelectorParser<'a> {
- type Impl = SelectorImpl;
- type Error = StyleParseErrorKind<'i>;
-
- fn parse_non_ts_pseudo_class(
- &self,
- location: SourceLocation,
- name: CowRcStr<'i>,
- ) -> Result<NonTSPseudoClass, ParseError<'i>> {
- use self::NonTSPseudoClass::*;
- let pseudo_class = match_ignore_ascii_case! { &name,
- "active" => Active,
- "any-link" => AnyLink,
- "checked" => Checked,
- "valid" => Valid,
- "invalid" => Invalid,
- "defined" => Defined,
- "disabled" => Disabled,
- "enabled" => Enabled,
- "focus" => Focus,
- "fullscreen" => Fullscreen,
- "hover" => Hover,
- "indeterminate" => Indeterminate,
- "link" => Link,
- "placeholder-shown" => PlaceholderShown,
- "read-write" => ReadWrite,
- "read-only" => ReadOnly,
- "target" => Target,
- "visited" => Visited,
- "-servo-nonzero-border" => {
- if !self.in_user_agent_stylesheet() {
- return Err(location.new_custom_error(
- SelectorParseErrorKind::UnexpectedIdent("-servo-nonzero-border".into())
- ))
- }
- ServoNonZeroBorder
- },
- _ => return Err(location.new_custom_error(SelectorParseErrorKind::UnexpectedIdent(name.clone()))),
- };
-
- Ok(pseudo_class)
- }
-
- fn parse_non_ts_functional_pseudo_class<'t>(
- &self,
- name: CowRcStr<'i>,
- parser: &mut CssParser<'i, 't>,
- ) -> Result<NonTSPseudoClass, ParseError<'i>> {
- use self::NonTSPseudoClass::*;
- let pseudo_class = match_ignore_ascii_case! { &name,
- "lang" => {
- Lang(parser.expect_ident_or_string()?.as_ref().into())
- },
- _ => return Err(parser.new_custom_error(SelectorParseErrorKind::UnexpectedIdent(name.clone()))),
- };
-
- Ok(pseudo_class)
- }
-
- fn parse_pseudo_element(
- &self,
- location: SourceLocation,
- name: CowRcStr<'i>,
- ) -> Result<PseudoElement, ParseError<'i>> {
- use self::PseudoElement::*;
- let pseudo_element = match_ignore_ascii_case! { &name,
- "before" => Before,
- "after" => After,
- "selection" => Selection,
- "-servo-details-summary" => {
- if !self.in_user_agent_stylesheet() {
- return Err(location.new_custom_error(SelectorParseErrorKind::UnexpectedIdent(name.clone())))
- }
- DetailsSummary
- },
- "-servo-details-content" => {
- if !self.in_user_agent_stylesheet() {
- return Err(location.new_custom_error(SelectorParseErrorKind::UnexpectedIdent(name.clone())))
- }
- DetailsContent
- },
- "-servo-anonymous-box" => {
- if !self.in_user_agent_stylesheet() {
- return Err(location.new_custom_error(SelectorParseErrorKind::UnexpectedIdent(name.clone())))
- }
- ServoAnonymousBox
- },
- "-servo-legacy-text" => {
- if !self.in_user_agent_stylesheet() {
- return Err(location.new_custom_error(SelectorParseErrorKind::UnexpectedIdent(name.clone())))
- }
- ServoLegacyText
- },
- "-servo-legacy-input-text" => {
- if !self.in_user_agent_stylesheet() {
- return Err(location.new_custom_error(SelectorParseErrorKind::UnexpectedIdent(name.clone())))
- }
- ServoLegacyInputText
- },
- "-servo-legacy-table-wrapper" => {
- if !self.in_user_agent_stylesheet() {
- return Err(location.new_custom_error(SelectorParseErrorKind::UnexpectedIdent(name.clone())))
- }
- ServoLegacyTableWrapper
- },
- "-servo-legacy-anonymous-table-wrapper" => {
- if !self.in_user_agent_stylesheet() {
- return Err(location.new_custom_error(SelectorParseErrorKind::UnexpectedIdent(name.clone())))
- }
- ServoLegacyAnonymousTableWrapper
- },
- "-servo-legacy-anonymous-table" => {
- if !self.in_user_agent_stylesheet() {
- return Err(location.new_custom_error(SelectorParseErrorKind::UnexpectedIdent(name.clone())))
- }
- ServoLegacyAnonymousTable
- },
- "-servo-anonymous-table-row" => {
- if !self.in_user_agent_stylesheet() {
- return Err(location.new_custom_error(SelectorParseErrorKind::UnexpectedIdent(name.clone())))
- }
- ServoAnonymousTableRow
- },
- "-servo-anonymous-table-cell" => {
- if !self.in_user_agent_stylesheet() {
- return Err(location.new_custom_error(SelectorParseErrorKind::UnexpectedIdent(name.clone())))
- }
- ServoAnonymousTableCell
- },
- "-servo-legacy-anonymous-block" => {
- if !self.in_user_agent_stylesheet() {
- return Err(location.new_custom_error(SelectorParseErrorKind::UnexpectedIdent(name.clone())))
- }
- ServoLegacyAnonymousBlock
- },
- "-servo-legacy-inline-block-wrapper" => {
- if !self.in_user_agent_stylesheet() {
- return Err(location.new_custom_error(SelectorParseErrorKind::UnexpectedIdent(name.clone())))
- }
- ServoLegacyInlineBlockWrapper
- },
- "-servo-legacy-inline-absolute" => {
- if !self.in_user_agent_stylesheet() {
- return Err(location.new_custom_error(SelectorParseErrorKind::UnexpectedIdent(name.clone())))
- }
- ServoLegacyInlineAbsolute
- },
- _ => return Err(location.new_custom_error(SelectorParseErrorKind::UnexpectedIdent(name.clone())))
-
- };
-
- Ok(pseudo_element)
- }
-
- fn default_namespace(&self) -> Option<Namespace> {
- self.namespaces.default.as_ref().map(|ns| ns.clone())
- }
-
- fn namespace_for_prefix(&self, prefix: &Prefix) -> Option<Namespace> {
- self.namespaces.prefixes.get(prefix).cloned()
- }
-}
-
-impl SelectorImpl {
- /// A helper to traverse each eagerly cascaded pseudo-element, executing
- /// `fun` on it.
- #[inline]
- pub fn each_eagerly_cascaded_pseudo_element<F>(mut fun: F)
- where
- F: FnMut(PseudoElement),
- {
- for i in 0..EAGER_PSEUDO_COUNT {
- fun(PseudoElement::from_eager_index(i));
- }
- }
-}
-
-/// A map from elements to snapshots for the Servo style back-end.
-#[derive(Debug)]
-pub struct SnapshotMap(FxHashMap<OpaqueNode, ServoElementSnapshot>);
-
-impl SnapshotMap {
- /// Create a new empty `SnapshotMap`.
- pub fn new() -> Self {
- SnapshotMap(FxHashMap::default())
- }
-
- /// Get a snapshot given an element.
- pub fn get<T: TElement>(&self, el: &T) -> Option<&ServoElementSnapshot> {
- self.0.get(&el.as_node().opaque())
- }
-}
-
-impl Deref for SnapshotMap {
- type Target = FxHashMap<OpaqueNode, ServoElementSnapshot>;
-
- fn deref(&self) -> &Self::Target {
- &self.0
- }
-}
-
-impl DerefMut for SnapshotMap {
- fn deref_mut(&mut self) -> &mut Self::Target {
- &mut self.0
- }
-}
-
-/// Servo's version of an element snapshot.
-#[derive(Debug, Default, MallocSizeOf)]
-pub struct ServoElementSnapshot {
- /// The stored state of the element.
- pub state: Option<ElementState>,
- /// The set of stored attributes and its values.
- pub attrs: Option<Vec<(AttrIdentifier, AttrValue)>>,
- /// The set of changed attributes and its values.
- pub changed_attrs: Vec<LocalName>,
- /// Whether the class attribute changed or not.
- pub class_changed: bool,
- /// Whether the id attribute changed or not.
- pub id_changed: bool,
- /// Whether other attributes other than id or class changed or not.
- pub other_attributes_changed: bool,
-}
-
-impl ServoElementSnapshot {
- /// Create an empty element snapshot.
- pub fn new() -> Self {
- Self::default()
- }
-
- /// Returns whether the id attribute changed or not.
- pub fn id_changed(&self) -> bool {
- self.id_changed
- }
-
- /// Returns whether the class attribute changed or not.
- pub fn class_changed(&self) -> bool {
- self.class_changed
- }
-
- /// Returns whether other attributes other than id or class changed or not.
- pub fn other_attr_changed(&self) -> bool {
- self.other_attributes_changed
- }
-
- fn get_attr(&self, namespace: &Namespace, name: &LocalName) -> Option<&AttrValue> {
- self.attrs
- .as_ref()
- .unwrap()
- .iter()
- .find(|&&(ref ident, _)| ident.local_name == *name && ident.namespace == *namespace)
- .map(|&(_, ref v)| v)
- }
-
- /// Executes the callback once for each attribute that changed.
- #[inline]
- pub fn each_attr_changed<F>(&self, mut callback: F)
- where
- F: FnMut(&LocalName),
- {
- for name in &self.changed_attrs {
- callback(name)
- }
- }
-
- fn any_attr_ignore_ns<F>(&self, name: &LocalName, mut f: F) -> bool
- where
- F: FnMut(&AttrValue) -> bool,
- {
- self.attrs
- .as_ref()
- .unwrap()
- .iter()
- .any(|&(ref ident, ref v)| ident.local_name == *name && f(v))
- }
-}
-
-impl ElementSnapshot for ServoElementSnapshot {
- fn state(&self) -> Option<ElementState> {
- self.state.clone()
- }
-
- fn has_attrs(&self) -> bool {
- self.attrs.is_some()
- }
-
- fn id_attr(&self) -> Option<&Atom> {
- self.get_attr(&ns!(), &local_name!("id"))
- .map(|v| v.as_atom())
- }
-
- fn is_part(&self, _name: &AtomIdent) -> bool {
- false
- }
-
- fn imported_part(&self, _: &AtomIdent) -> Option<AtomIdent> {
- None
- }
-
- fn has_class(&self, name: &AtomIdent, case_sensitivity: CaseSensitivity) -> bool {
- self.get_attr(&ns!(), &local_name!("class"))
- .map_or(false, |v| {
- v.as_tokens()
- .iter()
- .any(|atom| case_sensitivity.eq_atom(atom, name))
- })
- }
-
- fn each_class<F>(&self, mut callback: F)
- where
- F: FnMut(&AtomIdent),
- {
- if let Some(v) = self.get_attr(&ns!(), &local_name!("class")) {
- for class in v.as_tokens() {
- callback(AtomIdent::cast(class));
- }
- }
- }
-
- fn lang_attr(&self) -> Option<SelectorAttrValue> {
- self.get_attr(&ns!(xml), &local_name!("lang"))
- .or_else(|| self.get_attr(&ns!(), &local_name!("lang")))
- .map(|v| SelectorAttrValue::from(v as &str))
- }
-}
-
-impl ServoElementSnapshot {
- /// selectors::Element::attr_matches
- pub fn attr_matches(
- &self,
- ns: &NamespaceConstraint<&Namespace>,
- local_name: &LocalName,
- operation: &AttrSelectorOperation<&AtomString>,
- ) -> bool {
- match *ns {
- NamespaceConstraint::Specific(ref ns) => self
- .get_attr(ns, local_name)
- .map_or(false, |value| value.eval_selector(operation)),
- NamespaceConstraint::Any => {
- self.any_attr_ignore_ns(local_name, |value| value.eval_selector(operation))
- },
- }
- }
-}
-
-/// Returns whether the language is matched, as defined by
-/// [RFC 4647](https://tools.ietf.org/html/rfc4647#section-3.3.2).
-pub fn extended_filtering(tag: &str, range: &str) -> bool {
- range.split(',').any(|lang_range| {
- // step 1
- let mut range_subtags = lang_range.split('\x2d');
- let mut tag_subtags = tag.split('\x2d');
-
- // step 2
- // Note: [Level-4 spec](https://drafts.csswg.org/selectors/#lang-pseudo) check for wild card
- if let (Some(range_subtag), Some(tag_subtag)) = (range_subtags.next(), tag_subtags.next()) {
- if !(range_subtag.eq_ignore_ascii_case(tag_subtag) ||
- range_subtag.eq_ignore_ascii_case("*"))
- {
- return false;
- }
- }
-
- let mut current_tag_subtag = tag_subtags.next();
-
- // step 3
- for range_subtag in range_subtags {
- // step 3a
- if range_subtag == "*" {
- continue;
- }
- match current_tag_subtag.clone() {
- Some(tag_subtag) => {
- // step 3c
- if range_subtag.eq_ignore_ascii_case(tag_subtag) {
- current_tag_subtag = tag_subtags.next();
- continue;
- }
- // step 3d
- if tag_subtag.len() == 1 {
- return false;
- }
- // else step 3e - continue with loop
- current_tag_subtag = tag_subtags.next();
- if current_tag_subtag.is_none() {
- return false;
- }
- },
- // step 3b
- None => {
- return false;
- },
- }
- }
- // step 4
- true
- })
-}
diff --git a/components/style/servo/url.rs b/components/style/servo/url.rs
deleted file mode 100644
index ab1ef8435f7..00000000000
--- a/components/style/servo/url.rs
+++ /dev/null
@@ -1,246 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
-
-//! Common handling for the specified value CSS url() values.
-
-use crate::parser::{Parse, ParserContext};
-use crate::stylesheets::CorsMode;
-use crate::values::computed::{Context, ToComputedValue};
-use cssparser::Parser;
-use servo_arc::Arc;
-use std::fmt::{self, Write};
-use style_traits::{CssWriter, ParseError, ToCss};
-use to_shmem::{SharedMemoryBuilder, ToShmem};
-use url::Url;
-
-/// A CSS url() value for servo.
-///
-/// Servo eagerly resolves SpecifiedUrls, which it can then take advantage of
-/// when computing values. In contrast, Gecko uses a different URL backend, so
-/// eagerly resolving with rust-url would be duplicated work.
-///
-/// However, this approach is still not necessarily optimal: See
-/// <https://bugzilla.mozilla.org/show_bug.cgi?id=1347435#c6>
-///
-/// TODO(emilio): This should be shrunk by making CssUrl a wrapper type of an
-/// arc, and keep the serialization in that Arc. See gecko/url.rs for example.
-#[derive(Clone, Debug, Deserialize, MallocSizeOf, Serialize, SpecifiedValueInfo)]
-pub struct CssUrl {
- /// The original URI. This might be optional since we may insert computed
- /// values of images into the cascade directly, and we don't bother to
- /// convert their serialization.
- ///
- /// Refcounted since cloning this should be cheap and data: uris can be
- /// really large.
- #[ignore_malloc_size_of = "Arc"]
- original: Option<Arc<String>>,
-
- /// The resolved value for the url, if valid.
- #[ignore_malloc_size_of = "Arc"]
- resolved: Option<Arc<Url>>,
-}
-
-impl ToShmem for CssUrl {
- fn to_shmem(&self, _builder: &mut SharedMemoryBuilder) -> to_shmem::Result<Self> {
- unimplemented!("If servo wants to share stylesheets across processes, ToShmem for Url must be implemented");
- }
-}
-
-impl CssUrl {
- /// Try to parse a URL from a string value that is a valid CSS token for a
- /// URL.
- ///
- /// FIXME(emilio): Should honor CorsMode.
- pub fn parse_from_string(url: String, context: &ParserContext, _: CorsMode) -> Self {
- let serialization = Arc::new(url);
- let resolved = context.url_data.0.join(&serialization).ok().map(Arc::new);
- CssUrl {
- original: Some(serialization),
- resolved: resolved,
- }
- }
-
- /// Returns true if the URL is definitely invalid. For Servo URLs, we can
- /// use its |resolved| status.
- pub fn is_invalid(&self) -> bool {
- self.resolved.is_none()
- }
-
- /// Returns true if this URL looks like a fragment.
- /// See https://drafts.csswg.org/css-values/#local-urls
- ///
- /// Since Servo currently stores resolved URLs, this is hard to implement. We
- /// either need to change servo to lazily resolve (like Gecko), or note this
- /// information in the tokenizer.
- pub fn is_fragment(&self) -> bool {
- error!("Can't determine whether the url is a fragment.");
- false
- }
-
- /// Returns the resolved url if it was valid.
- pub fn url(&self) -> Option<&Arc<Url>> {
- self.resolved.as_ref()
- }
-
- /// Return the resolved url as string, or the empty string if it's invalid.
- ///
- /// TODO(emilio): Should we return the original one if needed?
- pub fn as_str(&self) -> &str {
- match self.resolved {
- Some(ref url) => url.as_str(),
- None => "",
- }
- }
-
- /// Creates an already specified url value from an already resolved URL
- /// for insertion in the cascade.
- pub fn for_cascade(url: Arc<::url::Url>) -> Self {
- CssUrl {
- original: None,
- resolved: Some(url),
- }
- }
-
- /// Gets a new url from a string for unit tests.
- pub fn new_for_testing(url: &str) -> Self {
- CssUrl {
- original: Some(Arc::new(url.into())),
- resolved: ::url::Url::parse(url).ok().map(Arc::new),
- }
- }
-
- /// Parses a URL request and records that the corresponding request needs to
- /// be CORS-enabled.
- ///
- /// This is only for shape images and masks in Gecko, thus unimplemented for
- /// now so somebody notices when trying to do so.
- pub fn parse_with_cors_mode<'i, 't>(
- context: &ParserContext,
- input: &mut Parser<'i, 't>,
- cors_mode: CorsMode,
- ) -> Result<Self, ParseError<'i>> {
- let url = input.expect_url()?;
- Ok(Self::parse_from_string(
- url.as_ref().to_owned(),
- context,
- cors_mode,
- ))
- }
-}
-
-impl Parse for CssUrl {
- fn parse<'i, 't>(
- context: &ParserContext,
- input: &mut Parser<'i, 't>,
- ) -> Result<Self, ParseError<'i>> {
- Self::parse_with_cors_mode(context, input, CorsMode::None)
- }
-}
-
-impl PartialEq for CssUrl {
- fn eq(&self, other: &Self) -> bool {
- // TODO(emilio): maybe we care about equality of the specified values if
- // present? Seems not.
- self.resolved == other.resolved
- }
-}
-
-impl Eq for CssUrl {}
-
-impl ToCss for CssUrl {
- fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
- where
- W: Write,
- {
- let string = match self.original {
- Some(ref original) => &**original,
- None => match self.resolved {
- Some(ref url) => url.as_str(),
- // This can only happen if the url wasn't specified by the
- // user *and* it's an invalid url that has been transformed
- // back to specified value via the "uncompute" functionality.
- None => "about:invalid",
- },
- };
-
- dest.write_str("url(")?;
- string.to_css(dest)?;
- dest.write_char(')')
- }
-}
-
-/// A specified url() value for servo.
-pub type SpecifiedUrl = CssUrl;
-
-impl ToComputedValue for SpecifiedUrl {
- type ComputedValue = ComputedUrl;
-
- // If we can't resolve the URL from the specified one, we fall back to the original
- // but still return it as a ComputedUrl::Invalid
- fn to_computed_value(&self, _: &Context) -> Self::ComputedValue {
- match self.resolved {
- Some(ref url) => ComputedUrl::Valid(url.clone()),
- None => match self.original {
- Some(ref url) => ComputedUrl::Invalid(url.clone()),
- None => {
- unreachable!("Found specified url with neither resolved or original URI!");
- },
- },
- }
- }
-
- fn from_computed_value(computed: &ComputedUrl) -> Self {
- match *computed {
- ComputedUrl::Valid(ref url) => SpecifiedUrl {
- original: None,
- resolved: Some(url.clone()),
- },
- ComputedUrl::Invalid(ref url) => SpecifiedUrl {
- original: Some(url.clone()),
- resolved: None,
- },
- }
- }
-}
-
-/// A specified image url() value for servo.
-pub type SpecifiedImageUrl = CssUrl;
-
-/// The computed value of a CSS `url()`, resolved relative to the stylesheet URL.
-#[derive(Clone, Debug, Deserialize, MallocSizeOf, PartialEq, Serialize)]
-pub enum ComputedUrl {
- /// The `url()` was invalid or it wasn't specified by the user.
- Invalid(#[ignore_malloc_size_of = "Arc"] Arc<String>),
- /// The resolved `url()` relative to the stylesheet URL.
- Valid(#[ignore_malloc_size_of = "Arc"] Arc<Url>),
-}
-
-impl ComputedUrl {
- /// Returns the resolved url if it was valid.
- pub fn url(&self) -> Option<&Arc<Url>> {
- match *self {
- ComputedUrl::Valid(ref url) => Some(url),
- _ => None,
- }
- }
-}
-
-impl ToCss for ComputedUrl {
- fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
- where
- W: Write,
- {
- let string = match *self {
- ComputedUrl::Valid(ref url) => url.as_str(),
- ComputedUrl::Invalid(ref invalid_string) => invalid_string,
- };
-
- dest.write_str("url(")?;
- string.to_css(dest)?;
- dest.write_char(')')
- }
-}
-
-/// The computed value of a CSS `url()` for image.
-pub type ComputedImageUrl = ComputedUrl;
diff --git a/components/style/shared_lock.rs b/components/style/shared_lock.rs
deleted file mode 100644
index 55708a9f7bb..00000000000
--- a/components/style/shared_lock.rs
+++ /dev/null
@@ -1,374 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
-
-//! Different objects protected by the same lock
-
-use crate::str::{CssString, CssStringWriter};
-use crate::stylesheets::Origin;
-#[cfg(feature = "gecko")]
-use atomic_refcell::{AtomicRef, AtomicRefCell, AtomicRefMut};
-#[cfg(feature = "servo")]
-use parking_lot::RwLock;
-use servo_arc::Arc;
-use std::cell::UnsafeCell;
-use std::fmt;
-#[cfg(feature = "servo")]
-use std::mem;
-#[cfg(feature = "gecko")]
-use std::ptr;
-use to_shmem::{SharedMemoryBuilder, ToShmem};
-
-/// A shared read/write lock that can protect multiple objects.
-///
-/// In Gecko builds, we don't need the blocking behavior, just the safety. As
-/// such we implement this with an AtomicRefCell instead in Gecko builds,
-/// which is ~2x as fast, and panics (rather than deadlocking) when things go
-/// wrong (which is much easier to debug on CI).
-///
-/// Servo needs the blocking behavior for its unsynchronized animation setup,
-/// but that may not be web-compatible and may need to be changed (at which
-/// point Servo could use AtomicRefCell too).
-///
-/// Gecko also needs the ability to have "read only" SharedRwLocks, which are
-/// used for objects stored in (read only) shared memory. Attempting to acquire
-/// write access to objects protected by a read only SharedRwLock will panic.
-#[derive(Clone)]
-#[cfg_attr(feature = "servo", derive(MallocSizeOf))]
-pub struct SharedRwLock {
- #[cfg(feature = "servo")]
- #[cfg_attr(feature = "servo", ignore_malloc_size_of = "Arc")]
- arc: Arc<RwLock<()>>,
-
- #[cfg(feature = "gecko")]
- cell: Option<Arc<AtomicRefCell<SomethingZeroSizedButTyped>>>,
-}
-
-#[cfg(feature = "gecko")]
-struct SomethingZeroSizedButTyped;
-
-impl fmt::Debug for SharedRwLock {
- fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
- f.write_str("SharedRwLock")
- }
-}
-
-impl SharedRwLock {
- /// Create a new shared lock (servo).
- #[cfg(feature = "servo")]
- pub fn new() -> Self {
- SharedRwLock {
- arc: Arc::new(RwLock::new(())),
- }
- }
-
- /// Create a new shared lock (gecko).
- #[cfg(feature = "gecko")]
- pub fn new() -> Self {
- SharedRwLock {
- cell: Some(Arc::new(AtomicRefCell::new(SomethingZeroSizedButTyped))),
- }
- }
-
- /// Create a new global shared lock (servo).
- #[cfg(feature = "servo")]
- pub fn new_leaked() -> Self {
- SharedRwLock {
- arc: Arc::new_leaked(RwLock::new(())),
- }
- }
-
- /// Create a new global shared lock (gecko).
- #[cfg(feature = "gecko")]
- pub fn new_leaked() -> Self {
- SharedRwLock {
- cell: Some(Arc::new_leaked(AtomicRefCell::new(
- SomethingZeroSizedButTyped,
- ))),
- }
- }
-
- /// Create a new read-only shared lock (gecko).
- #[cfg(feature = "gecko")]
- pub fn read_only() -> Self {
- SharedRwLock { cell: None }
- }
-
- #[cfg(feature = "gecko")]
- #[inline]
- fn ptr(&self) -> *const SomethingZeroSizedButTyped {
- self.cell
- .as_ref()
- .map(|cell| cell.as_ptr() as *const _)
- .unwrap_or(ptr::null())
- }
-
- /// Wrap the given data to make its access protected by this lock.
- pub fn wrap<T>(&self, data: T) -> Locked<T> {
- Locked {
- shared_lock: self.clone(),
- data: UnsafeCell::new(data),
- }
- }
-
- /// Obtain the lock for reading (servo).
- #[cfg(feature = "servo")]
- pub fn read(&self) -> SharedRwLockReadGuard {
- mem::forget(self.arc.read());
- SharedRwLockReadGuard(self)
- }
-
- /// Obtain the lock for reading (gecko).
- #[cfg(feature = "gecko")]
- pub fn read(&self) -> SharedRwLockReadGuard {
- SharedRwLockReadGuard(self.cell.as_ref().map(|cell| cell.borrow()))
- }
-
- /// Obtain the lock for writing (servo).
- #[cfg(feature = "servo")]
- pub fn write(&self) -> SharedRwLockWriteGuard {
- mem::forget(self.arc.write());
- SharedRwLockWriteGuard(self)
- }
-
- /// Obtain the lock for writing (gecko).
- #[cfg(feature = "gecko")]
- pub fn write(&self) -> SharedRwLockWriteGuard {
- SharedRwLockWriteGuard(self.cell.as_ref().unwrap().borrow_mut())
- }
-}
-
-/// Proof that a shared lock was obtained for reading (servo).
-#[cfg(feature = "servo")]
-pub struct SharedRwLockReadGuard<'a>(&'a SharedRwLock);
-/// Proof that a shared lock was obtained for reading (gecko).
-#[cfg(feature = "gecko")]
-pub struct SharedRwLockReadGuard<'a>(Option<AtomicRef<'a, SomethingZeroSizedButTyped>>);
-#[cfg(feature = "servo")]
-impl<'a> Drop for SharedRwLockReadGuard<'a> {
- fn drop(&mut self) {
- // Unsafe: self.lock is private to this module, only ever set after `read()`,
- // and never copied or cloned (see `compile_time_assert` below).
- unsafe { self.0.arc.force_unlock_read() }
- }
-}
-
-impl<'a> SharedRwLockReadGuard<'a> {
- #[inline]
- #[cfg(feature = "gecko")]
- fn ptr(&self) -> *const SomethingZeroSizedButTyped {
- self.0
- .as_ref()
- .map(|r| &**r as *const _)
- .unwrap_or(ptr::null())
- }
-}
-
-/// Proof that a shared lock was obtained for writing (servo).
-#[cfg(feature = "servo")]
-pub struct SharedRwLockWriteGuard<'a>(&'a SharedRwLock);
-/// Proof that a shared lock was obtained for writing (gecko).
-#[cfg(feature = "gecko")]
-pub struct SharedRwLockWriteGuard<'a>(AtomicRefMut<'a, SomethingZeroSizedButTyped>);
-#[cfg(feature = "servo")]
-impl<'a> Drop for SharedRwLockWriteGuard<'a> {
- fn drop(&mut self) {
- // Unsafe: self.lock is private to this module, only ever set after `write()`,
- // and never copied or cloned (see `compile_time_assert` below).
- unsafe { self.0.arc.force_unlock_write() }
- }
-}
-
-/// Data protect by a shared lock.
-pub struct Locked<T> {
- shared_lock: SharedRwLock,
- data: UnsafeCell<T>,
-}
-
-// Unsafe: the data inside `UnsafeCell` is only accessed in `read_with` and `write_with`,
-// where guards ensure synchronization.
-unsafe impl<T: Send> Send for Locked<T> {}
-unsafe impl<T: Send + Sync> Sync for Locked<T> {}
-
-impl<T: fmt::Debug> fmt::Debug for Locked<T> {
- fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
- let guard = self.shared_lock.read();
- self.read_with(&guard).fmt(f)
- }
-}
-
-impl<T> Locked<T> {
- #[cfg(feature = "gecko")]
- #[inline]
- fn is_read_only_lock(&self) -> bool {
- self.shared_lock.cell.is_none()
- }
-
- #[cfg(feature = "servo")]
- fn same_lock_as(&self, lock: &SharedRwLock) -> bool {
- Arc::ptr_eq(&self.shared_lock.arc, &lock.arc)
- }
-
- #[cfg(feature = "gecko")]
- fn same_lock_as(&self, ptr: *const SomethingZeroSizedButTyped) -> bool {
- ptr::eq(self.shared_lock.ptr(), ptr)
- }
-
- /// Access the data for reading.
- pub fn read_with<'a>(&'a self, guard: &'a SharedRwLockReadGuard) -> &'a T {
- #[cfg(feature = "gecko")]
- assert!(
- self.is_read_only_lock() || self.same_lock_as(guard.ptr()),
- "Locked::read_with called with a guard from an unrelated SharedRwLock: {:?} vs. {:?}",
- self.shared_lock.ptr(),
- guard.ptr(),
- );
- #[cfg(not(feature = "gecko"))]
- assert!(self.same_lock_as(&guard.0));
-
- let ptr = self.data.get();
-
- // Unsafe:
- //
- // * The guard guarantees that the lock is taken for reading,
- // and we’ve checked that it’s the correct lock.
- // * The returned reference borrows *both* the data and the guard,
- // so that it can outlive neither.
- unsafe { &*ptr }
- }
-
- /// Access the data for reading without verifying the lock. Use with caution.
- #[cfg(feature = "gecko")]
- pub unsafe fn read_unchecked<'a>(&'a self) -> &'a T {
- let ptr = self.data.get();
- &*ptr
- }
-
- /// Access the data for writing.
- pub fn write_with<'a>(&'a self, guard: &'a mut SharedRwLockWriteGuard) -> &'a mut T {
- #[cfg(feature = "gecko")]
- assert!(
- !self.is_read_only_lock() && self.same_lock_as(&*guard.0),
- "Locked::write_with called with a guard from a read only or unrelated SharedRwLock"
- );
- #[cfg(not(feature = "gecko"))]
- assert!(self.same_lock_as(&guard.0));
-
- let ptr = self.data.get();
-
- // Unsafe:
- //
- // * The guard guarantees that the lock is taken for writing,
- // and we’ve checked that it’s the correct lock.
- // * The returned reference borrows *both* the data and the guard,
- // so that it can outlive neither.
- // * We require a mutable borrow of the guard,
- // so that one write guard can only be used once at a time.
- unsafe { &mut *ptr }
- }
-}
-
-#[cfg(feature = "gecko")]
-impl<T: ToShmem> ToShmem for Locked<T> {
- fn to_shmem(&self, builder: &mut SharedMemoryBuilder) -> to_shmem::Result<Self> {
- use std::mem::ManuallyDrop;
-
- let guard = self.shared_lock.read();
- Ok(ManuallyDrop::new(Locked {
- shared_lock: SharedRwLock::read_only(),
- data: UnsafeCell::new(ManuallyDrop::into_inner(
- self.read_with(&guard).to_shmem(builder)?,
- )),
- }))
- }
-}
-
-#[cfg(feature = "servo")]
-impl<T: ToShmem> ToShmem for Locked<T> {
- fn to_shmem(&self, _builder: &mut SharedMemoryBuilder) -> to_shmem::Result<Self> {
- panic!("ToShmem not supported in Servo currently")
- }
-}
-
-#[allow(dead_code)]
-mod compile_time_assert {
- use super::{SharedRwLockReadGuard, SharedRwLockWriteGuard};
-
- trait Marker1 {}
- impl<T: Clone> Marker1 for T {}
- impl<'a> Marker1 for SharedRwLockReadGuard<'a> {} // Assert SharedRwLockReadGuard: !Clone
- impl<'a> Marker1 for SharedRwLockWriteGuard<'a> {} // Assert SharedRwLockWriteGuard: !Clone
-
- trait Marker2 {}
- impl<T: Copy> Marker2 for T {}
- impl<'a> Marker2 for SharedRwLockReadGuard<'a> {} // Assert SharedRwLockReadGuard: !Copy
- impl<'a> Marker2 for SharedRwLockWriteGuard<'a> {} // Assert SharedRwLockWriteGuard: !Copy
-}
-
-/// Like ToCss, but with a lock guard given by the caller, and with the writer specified
-/// concretely rather than with a parameter.
-pub trait ToCssWithGuard {
- /// Serialize `self` in CSS syntax, writing to `dest`, using the given lock guard.
- fn to_css(&self, guard: &SharedRwLockReadGuard, dest: &mut CssStringWriter) -> fmt::Result;
-
- /// Serialize `self` in CSS syntax using the given lock guard and return a string.
- ///
- /// (This is a convenience wrapper for `to_css` and probably should not be overridden.)
- #[inline]
- fn to_css_string(&self, guard: &SharedRwLockReadGuard) -> CssString {
- let mut s = CssString::new();
- self.to_css(guard, &mut s).unwrap();
- s
- }
-}
-
-/// Parameters needed for deep clones.
-#[cfg(feature = "gecko")]
-pub struct DeepCloneParams {
- /// The new sheet we're cloning rules into.
- pub reference_sheet: *const crate::gecko_bindings::structs::StyleSheet,
-}
-
-/// Parameters needed for deep clones.
-#[cfg(feature = "servo")]
-pub struct DeepCloneParams;
-
-/// A trait to do a deep clone of a given CSS type. Gets a lock and a read
-/// guard, in order to be able to read and clone nested structures.
-pub trait DeepCloneWithLock: Sized {
- /// Deep clones this object.
- fn deep_clone_with_lock(
- &self,
- lock: &SharedRwLock,
- guard: &SharedRwLockReadGuard,
- params: &DeepCloneParams,
- ) -> Self;
-}
-
-/// Guards for a document
-#[derive(Clone)]
-pub struct StylesheetGuards<'a> {
- /// For author-origin stylesheets.
- pub author: &'a SharedRwLockReadGuard<'a>,
-
- /// For user-agent-origin and user-origin stylesheets
- pub ua_or_user: &'a SharedRwLockReadGuard<'a>,
-}
-
-impl<'a> StylesheetGuards<'a> {
- /// Get the guard for a given stylesheet origin.
- pub fn for_origin(&self, origin: Origin) -> &SharedRwLockReadGuard<'a> {
- match origin {
- Origin::Author => &self.author,
- _ => &self.ua_or_user,
- }
- }
-
- /// Same guard for all origins
- pub fn same(guard: &'a SharedRwLockReadGuard<'a>) -> Self {
- StylesheetGuards {
- author: guard,
- ua_or_user: guard,
- }
- }
-}
diff --git a/components/style/sharing/checks.rs b/components/style/sharing/checks.rs
deleted file mode 100644
index a691ac5c764..00000000000
--- a/components/style/sharing/checks.rs
+++ /dev/null
@@ -1,182 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
-
-//! Different checks done during the style sharing process in order to determine
-//! quickly whether it's worth to share style, and whether two different
-//! elements can indeed share the same style.
-
-use crate::bloom::StyleBloom;
-use crate::context::SharedStyleContext;
-use crate::dom::TElement;
-use crate::sharing::{StyleSharingCandidate, StyleSharingTarget};
-use selectors::NthIndexCache;
-
-/// Determines whether a target and a candidate have compatible parents for
-/// sharing.
-pub fn parents_allow_sharing<E>(
- target: &mut StyleSharingTarget<E>,
- candidate: &mut StyleSharingCandidate<E>,
-) -> bool
-where
- E: TElement,
-{
- // If the identity of the parent style isn't equal, we can't share. We check
- // this first, because the result is cached.
- if target.parent_style_identity() != candidate.parent_style_identity() {
- return false;
- }
-
- // Siblings can always share.
- let parent = target.inheritance_parent().unwrap();
- let candidate_parent = candidate.element.inheritance_parent().unwrap();
- if parent == candidate_parent {
- return true;
- }
-
- // Cousins are a bit more complicated.
- //
- // The fact that the candidate is here means that its element does not anchor
- // the relative selector. However, it may have considered relative selector(s)
- // to compute its style, i.e. there's a rule `<..> :has(<..>) <..> candidate`.
- // In this case, evaluating style sharing requires evaluating the relative
- // selector for the target anyway.
- if candidate.considered_relative_selector {
- return false;
- }
-
- // If a parent element was already styled and we traversed past it without
- // restyling it, that may be because our clever invalidation logic was able
- // to prove that the styles of that element would remain unchanged despite
- // changes to the id or class attributes. However, style sharing relies in
- // the strong guarantee that all the classes and ids up the respective parent
- // chains are identical. As such, if we skipped styling for one (or both) of
- // the parents on this traversal, we can't share styles across cousins.
- //
- // This is a somewhat conservative check. We could tighten it by having the
- // invalidation logic explicitly flag elements for which it ellided styling.
- let parent_data = parent.borrow_data().unwrap();
- let candidate_parent_data = candidate_parent.borrow_data().unwrap();
- if !parent_data.safe_for_cousin_sharing() || !candidate_parent_data.safe_for_cousin_sharing() {
- return false;
- }
-
- true
-}
-
-/// Whether two elements have the same same style attribute (by pointer identity).
-pub fn have_same_style_attribute<E>(
- target: &mut StyleSharingTarget<E>,
- candidate: &mut StyleSharingCandidate<E>,
-) -> bool
-where
- E: TElement,
-{
- match (target.style_attribute(), candidate.style_attribute()) {
- (None, None) => true,
- (Some(_), None) | (None, Some(_)) => false,
- (Some(a), Some(b)) => &*a as *const _ == &*b as *const _,
- }
-}
-
-/// Whether two elements have the same same presentational attributes.
-pub fn have_same_presentational_hints<E>(
- target: &mut StyleSharingTarget<E>,
- candidate: &mut StyleSharingCandidate<E>,
-) -> bool
-where
- E: TElement,
-{
- target.pres_hints() == candidate.pres_hints()
-}
-
-/// Whether a given element has the same class attribute as a given candidate.
-///
-/// We don't try to share style across elements with different class attributes.
-pub fn have_same_class<E>(
- target: &mut StyleSharingTarget<E>,
- candidate: &mut StyleSharingCandidate<E>,
-) -> bool
-where
- E: TElement,
-{
- target.class_list() == candidate.class_list()
-}
-
-/// Whether a given element has the same part attribute as a given candidate.
-///
-/// We don't try to share style across elements with different part attributes.
-pub fn have_same_parts<E>(
- target: &mut StyleSharingTarget<E>,
- candidate: &mut StyleSharingCandidate<E>,
-) -> bool
-where
- E: TElement,
-{
- target.part_list() == candidate.part_list()
-}
-
-/// Whether a given element and a candidate match the same set of "revalidation"
-/// selectors.
-///
-/// Revalidation selectors are those that depend on the DOM structure, like
-/// :first-child, etc, or on attributes that we don't check off-hand (pretty
-/// much every attribute selector except `id` and `class`.
-#[inline]
-pub fn revalidate<E>(
- target: &mut StyleSharingTarget<E>,
- candidate: &mut StyleSharingCandidate<E>,
- shared_context: &SharedStyleContext,
- bloom: &StyleBloom<E>,
- nth_index_cache: &mut NthIndexCache,
-) -> bool
-where
- E: TElement,
-{
- let stylist = &shared_context.stylist;
-
- let for_element = target.revalidation_match_results(stylist, bloom, nth_index_cache);
-
- let for_candidate = candidate.revalidation_match_results(stylist, bloom, nth_index_cache);
-
- // This assert "ensures", to some extent, that the two candidates have
- // matched the same rulehash buckets, and as such, that the bits we're
- // comparing represent the same set of selectors.
- debug_assert_eq!(for_element.len(), for_candidate.len());
-
- for_element == for_candidate
-}
-
-/// Checks whether we might have rules for either of the two ids.
-#[inline]
-pub fn may_match_different_id_rules<E>(
- shared_context: &SharedStyleContext,
- element: E,
- candidate: E,
-) -> bool
-where
- E: TElement,
-{
- let element_id = element.id();
- let candidate_id = candidate.id();
-
- if element_id == candidate_id {
- return false;
- }
-
- let stylist = &shared_context.stylist;
-
- let may_have_rules_for_element = match element_id {
- Some(id) => stylist.may_have_rules_for_id(id, element),
- None => false,
- };
-
- if may_have_rules_for_element {
- return true;
- }
-
- match candidate_id {
- Some(id) => stylist.may_have_rules_for_id(id, candidate),
- None => false,
- }
-}
diff --git a/components/style/sharing/mod.rs b/components/style/sharing/mod.rs
deleted file mode 100644
index 9e5b9603d77..00000000000
--- a/components/style/sharing/mod.rs
+++ /dev/null
@@ -1,910 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
-
-//! Code related to the style sharing cache, an optimization that allows similar
-//! nodes to share style without having to run selector matching twice.
-//!
-//! The basic setup is as follows. We have an LRU cache of style sharing
-//! candidates. When we try to style a target element, we first check whether
-//! we can quickly determine that styles match something in this cache, and if
-//! so we just use the cached style information. This check is done with a
-//! StyleBloom filter set up for the target element, which may not be a correct
-//! state for the cached candidate element if they're cousins instead of
-//! siblings.
-//!
-//! The complicated part is determining that styles match. This is subject to
-//! the following constraints:
-//!
-//! 1) The target and candidate must be inheriting the same styles.
-//! 2) The target and candidate must have exactly the same rules matching them.
-//! 3) The target and candidate must have exactly the same non-selector-based
-//! style information (inline styles, presentation hints).
-//! 4) The target and candidate must have exactly the same rules matching their
-//! pseudo-elements, because an element's style data points to the style
-//! data for its pseudo-elements.
-//!
-//! These constraints are satisfied in the following ways:
-//!
-//! * We check that the parents of the target and the candidate have the same
-//! computed style. This addresses constraint 1.
-//!
-//! * We check that the target and candidate have the same inline style and
-//! presentation hint declarations. This addresses constraint 3.
-//!
-//! * We ensure that a target matches a candidate only if they have the same
-//! matching result for all selectors that target either elements or the
-//! originating elements of pseudo-elements. This addresses constraint 4
-//! (because it prevents a target that has pseudo-element styles from matching
-//! a candidate that has different pseudo-element styles) as well as
-//! constraint 2.
-//!
-//! The actual checks that ensure that elements match the same rules are
-//! conceptually split up into two pieces. First, we do various checks on
-//! elements that make sure that the set of possible rules in all selector maps
-//! in the stylist (for normal styling and for pseudo-elements) that might match
-//! the two elements is the same. For example, we enforce that the target and
-//! candidate must have the same localname and namespace. Second, we have a
-//! selector map of "revalidation selectors" that the stylist maintains that we
-//! actually match against the target and candidate and then check whether the
-//! two sets of results were the same. Due to the up-front selector map checks,
-//! we know that the target and candidate will be matched against the same exact
-//! set of revalidation selectors, so the match result arrays can be compared
-//! directly.
-//!
-//! It's very important that a selector be added to the set of revalidation
-//! selectors any time there are two elements that could pass all the up-front
-//! checks but match differently against some ComplexSelector in the selector.
-//! If that happens, then they can have descendants that might themselves pass
-//! the up-front checks but would have different matching results for the
-//! selector in question. In this case, "descendants" includes pseudo-elements,
-//! so there is a single selector map of revalidation selectors that includes
-//! both selectors targeting elements and selectors targeting pseudo-element
-//! originating elements. We ensure that the pseudo-element parts of all these
-//! selectors are effectively stripped off, so that matching them all against
-//! elements makes sense.
-
-use crate::applicable_declarations::ApplicableDeclarationBlock;
-use crate::bloom::StyleBloom;
-use crate::computed_value_flags::ComputedValueFlags;
-use crate::context::{SharedStyleContext, StyleContext};
-use crate::dom::{SendElement, TElement};
-use crate::properties::ComputedValues;
-use crate::rule_tree::StrongRuleNode;
-use crate::style_resolver::{PrimaryStyle, ResolvedElementStyles};
-use crate::stylist::Stylist;
-use crate::values::AtomIdent;
-use atomic_refcell::{AtomicRefCell, AtomicRefMut};
-use owning_ref::OwningHandle;
-use selectors::matching::{NeedsSelectorFlags, VisitedHandlingMode};
-use selectors::NthIndexCache;
-use servo_arc::Arc;
-use smallbitvec::SmallBitVec;
-use smallvec::SmallVec;
-use std::marker::PhantomData;
-use std::mem::{self, ManuallyDrop};
-use std::ops::Deref;
-use std::ptr::NonNull;
-use uluru::LRUCache;
-
-mod checks;
-
-/// The amount of nodes that the style sharing candidate cache should hold at
-/// most.
-///
-/// The cache size was chosen by measuring style sharing and resulting
-/// performance on a few pages; sizes up to about 32 were giving good sharing
-/// improvements (e.g. 3x fewer styles having to be resolved than at size 8) and
-/// slight performance improvements. Sizes larger than 32 haven't really been
-/// tested.
-pub const SHARING_CACHE_SIZE: usize = 32;
-
-/// Opaque pointer type to compare ComputedValues identities.
-#[derive(Clone, Debug, Eq, PartialEq)]
-pub struct OpaqueComputedValues(NonNull<()>);
-
-unsafe impl Send for OpaqueComputedValues {}
-unsafe impl Sync for OpaqueComputedValues {}
-
-impl OpaqueComputedValues {
- fn from(cv: &ComputedValues) -> Self {
- let p =
- unsafe { NonNull::new_unchecked(cv as *const ComputedValues as *const () as *mut ()) };
- OpaqueComputedValues(p)
- }
-
- fn eq(&self, cv: &ComputedValues) -> bool {
- Self::from(cv) == *self
- }
-}
-
-/// Some data we want to avoid recomputing all the time while trying to share
-/// style.
-#[derive(Debug, Default)]
-pub struct ValidationData {
- /// The class list of this element.
- ///
- /// TODO(emilio): Maybe check whether rules for these classes apply to the
- /// element?
- class_list: Option<SmallVec<[AtomIdent; 5]>>,
-
- /// The part list of this element.
- ///
- /// TODO(emilio): Maybe check whether rules with these part names apply to
- /// the element?
- part_list: Option<SmallVec<[AtomIdent; 5]>>,
-
- /// The list of presentational attributes of the element.
- pres_hints: Option<SmallVec<[ApplicableDeclarationBlock; 5]>>,
-
- /// The pointer identity of the parent ComputedValues.
- parent_style_identity: Option<OpaqueComputedValues>,
-
- /// The cached result of matching this entry against the revalidation
- /// selectors.
- revalidation_match_results: Option<SmallBitVec>,
-}
-
-impl ValidationData {
- /// Move the cached data to a new instance, and return it.
- pub fn take(&mut self) -> Self {
- mem::replace(self, Self::default())
- }
-
- /// Get or compute the list of presentational attributes associated with
- /// this element.
- pub fn pres_hints<E>(&mut self, element: E) -> &[ApplicableDeclarationBlock]
- where
- E: TElement,
- {
- self.pres_hints.get_or_insert_with(|| {
- let mut pres_hints = SmallVec::new();
- element.synthesize_presentational_hints_for_legacy_attributes(
- VisitedHandlingMode::AllLinksUnvisited,
- &mut pres_hints,
- );
- pres_hints
- })
- }
-
- /// Get or compute the part-list associated with this element.
- pub fn part_list<E>(&mut self, element: E) -> &[AtomIdent]
- where
- E: TElement,
- {
- if !element.has_part_attr() {
- return &[];
- }
- self.part_list.get_or_insert_with(|| {
- let mut list = SmallVec::<[_; 5]>::new();
- element.each_part(|p| list.push(p.clone()));
- // See below for the reasoning.
- if !list.spilled() {
- list.sort_unstable_by_key(|a| a.get_hash());
- }
- list
- })
- }
-
- /// Get or compute the class-list associated with this element.
- pub fn class_list<E>(&mut self, element: E) -> &[AtomIdent]
- where
- E: TElement,
- {
- self.class_list.get_or_insert_with(|| {
- let mut list = SmallVec::<[_; 5]>::new();
- element.each_class(|c| list.push(c.clone()));
- // Assuming there are a reasonable number of classes (we use the
- // inline capacity as "reasonable number"), sort them to so that
- // we don't mistakenly reject sharing candidates when one element
- // has "foo bar" and the other has "bar foo".
- if !list.spilled() {
- list.sort_unstable_by_key(|a| a.get_hash());
- }
- list
- })
- }
-
- /// Get or compute the parent style identity.
- pub fn parent_style_identity<E>(&mut self, el: E) -> OpaqueComputedValues
- where
- E: TElement,
- {
- self.parent_style_identity
- .get_or_insert_with(|| {
- let parent = el.inheritance_parent().unwrap();
- let values =
- OpaqueComputedValues::from(parent.borrow_data().unwrap().styles.primary());
- values
- })
- .clone()
- }
-
- /// Computes the revalidation results if needed, and returns it.
- /// Inline so we know at compile time what bloom_known_valid is.
- #[inline]
- fn revalidation_match_results<E>(
- &mut self,
- element: E,
- stylist: &Stylist,
- bloom: &StyleBloom<E>,
- nth_index_cache: &mut NthIndexCache,
- bloom_known_valid: bool,
- needs_selector_flags: NeedsSelectorFlags,
- ) -> &SmallBitVec
- where
- E: TElement,
- {
- self.revalidation_match_results.get_or_insert_with(|| {
- // The bloom filter may already be set up for our element.
- // If it is, use it. If not, we must be in a candidate
- // (i.e. something in the cache), and the element is one
- // of our cousins, not a sibling. In that case, we'll
- // just do revalidation selector matching without a bloom
- // filter, to avoid thrashing the filter.
- let bloom_to_use = if bloom_known_valid {
- debug_assert_eq!(bloom.current_parent(), element.traversal_parent());
- Some(bloom.filter())
- } else {
- if bloom.current_parent() == element.traversal_parent() {
- Some(bloom.filter())
- } else {
- None
- }
- };
- stylist.match_revalidation_selectors(
- element,
- bloom_to_use,
- nth_index_cache,
- needs_selector_flags,
- )
- })
- }
-}
-
-/// Information regarding a style sharing candidate, that is, an entry in the
-/// style sharing cache.
-///
-/// Note that this information is stored in TLS and cleared after the traversal,
-/// and once here, the style information of the element is immutable, so it's
-/// safe to access.
-///
-/// Important: If you change the members/layout here, You need to do the same for
-/// FakeCandidate below.
-#[derive(Debug)]
-pub struct StyleSharingCandidate<E: TElement> {
- /// The element.
- element: E,
- validation_data: ValidationData,
- considered_relative_selector: bool,
-}
-
-struct FakeCandidate {
- _element: usize,
- _validation_data: ValidationData,
- _considered_relative_selector: bool,
-}
-
-impl<E: TElement> Deref for StyleSharingCandidate<E> {
- type Target = E;
-
- fn deref(&self) -> &Self::Target {
- &self.element
- }
-}
-
-impl<E: TElement> StyleSharingCandidate<E> {
- /// Get the classlist of this candidate.
- fn class_list(&mut self) -> &[AtomIdent] {
- self.validation_data.class_list(self.element)
- }
-
- /// Get the part list of this candidate.
- fn part_list(&mut self) -> &[AtomIdent] {
- self.validation_data.part_list(self.element)
- }
-
- /// Get the pres hints of this candidate.
- fn pres_hints(&mut self) -> &[ApplicableDeclarationBlock] {
- self.validation_data.pres_hints(self.element)
- }
-
- /// Get the parent style identity.
- fn parent_style_identity(&mut self) -> OpaqueComputedValues {
- self.validation_data.parent_style_identity(self.element)
- }
-
- /// Compute the bit vector of revalidation selector match results
- /// for this candidate.
- fn revalidation_match_results(
- &mut self,
- stylist: &Stylist,
- bloom: &StyleBloom<E>,
- nth_index_cache: &mut NthIndexCache,
- ) -> &SmallBitVec {
- self.validation_data.revalidation_match_results(
- self.element,
- stylist,
- bloom,
- nth_index_cache,
- /* bloom_known_valid = */ false,
- // The candidate must already have the right bits already, if
- // needed.
- NeedsSelectorFlags::No,
- )
- }
-}
-
-impl<E: TElement> PartialEq<StyleSharingCandidate<E>> for StyleSharingCandidate<E> {
- fn eq(&self, other: &Self) -> bool {
- self.element == other.element
- }
-}
-
-/// An element we want to test against the style sharing cache.
-pub struct StyleSharingTarget<E: TElement> {
- element: E,
- validation_data: ValidationData,
-}
-
-impl<E: TElement> Deref for StyleSharingTarget<E> {
- type Target = E;
-
- fn deref(&self) -> &Self::Target {
- &self.element
- }
-}
-
-impl<E: TElement> StyleSharingTarget<E> {
- /// Trivially construct a new StyleSharingTarget to test against the cache.
- pub fn new(element: E) -> Self {
- Self {
- element: element,
- validation_data: ValidationData::default(),
- }
- }
-
- fn class_list(&mut self) -> &[AtomIdent] {
- self.validation_data.class_list(self.element)
- }
-
- fn part_list(&mut self) -> &[AtomIdent] {
- self.validation_data.part_list(self.element)
- }
-
- /// Get the pres hints of this candidate.
- fn pres_hints(&mut self) -> &[ApplicableDeclarationBlock] {
- self.validation_data.pres_hints(self.element)
- }
-
- /// Get the parent style identity.
- fn parent_style_identity(&mut self) -> OpaqueComputedValues {
- self.validation_data.parent_style_identity(self.element)
- }
-
- fn revalidation_match_results(
- &mut self,
- stylist: &Stylist,
- bloom: &StyleBloom<E>,
- nth_index_cache: &mut NthIndexCache,
- ) -> &SmallBitVec {
- // It's important to set the selector flags. Otherwise, if we succeed in
- // sharing the style, we may not set the slow selector flags for the
- // right elements (which may not necessarily be |element|), causing
- // missed restyles after future DOM mutations.
- //
- // Gecko's test_bug534804.html exercises this. A minimal testcase is:
- // <style> #e:empty + span { ... } </style>
- // <span id="e">
- // <span></span>
- // </span>
- // <span></span>
- //
- // The style sharing cache will get a hit for the second span. When the
- // child span is subsequently removed from the DOM, missing selector
- // flags would cause us to miss the restyle on the second span.
- self.validation_data.revalidation_match_results(
- self.element,
- stylist,
- bloom,
- nth_index_cache,
- /* bloom_known_valid = */ true,
- NeedsSelectorFlags::Yes,
- )
- }
-
- /// Attempts to share a style with another node.
- pub fn share_style_if_possible(
- &mut self,
- context: &mut StyleContext<E>,
- ) -> Option<ResolvedElementStyles> {
- let cache = &mut context.thread_local.sharing_cache;
- let shared_context = &context.shared;
- let bloom_filter = &context.thread_local.bloom_filter;
- let nth_index_cache = &mut context.thread_local.nth_index_cache;
-
- if cache.dom_depth != bloom_filter.matching_depth() {
- debug!(
- "Can't share style, because DOM depth changed from {:?} to {:?}, element: {:?}",
- cache.dom_depth,
- bloom_filter.matching_depth(),
- self.element
- );
- return None;
- }
- debug_assert_eq!(
- bloom_filter.current_parent(),
- self.element.traversal_parent()
- );
-
- cache.share_style_if_possible(shared_context, bloom_filter, nth_index_cache, self)
- }
-
- /// Gets the validation data used to match against this target, if any.
- pub fn take_validation_data(&mut self) -> ValidationData {
- self.validation_data.take()
- }
-}
-
-struct SharingCacheBase<Candidate> {
- entries: LRUCache<Candidate, SHARING_CACHE_SIZE>,
-}
-
-impl<Candidate> Default for SharingCacheBase<Candidate> {
- fn default() -> Self {
- Self {
- entries: LRUCache::default(),
- }
- }
-}
-
-impl<Candidate> SharingCacheBase<Candidate> {
- fn clear(&mut self) {
- self.entries.clear();
- }
-
- fn is_empty(&self) -> bool {
- self.entries.len() == 0
- }
-}
-
-impl<E: TElement> SharingCache<E> {
- fn insert(
- &mut self,
- element: E,
- considered_relative_selector: bool,
- validation_data_holder: Option<&mut StyleSharingTarget<E>>,
- ) {
- let validation_data = match validation_data_holder {
- Some(v) => v.take_validation_data(),
- None => ValidationData::default(),
- };
- self.entries.insert(StyleSharingCandidate {
- element,
- considered_relative_selector,
- validation_data,
- });
- }
-}
-
-/// Style sharing caches are are large allocations, so we store them in thread-local
-/// storage such that they can be reused across style traversals. Ideally, we'd just
-/// stack-allocate these buffers with uninitialized memory, but right now rustc can't
-/// avoid memmoving the entire cache during setup, which gets very expensive. See
-/// issues like [1] and [2].
-///
-/// Given that the cache stores entries of type TElement, we transmute to usize
-/// before storing in TLS. This is safe as long as we make sure to empty the cache
-/// before we let it go.
-///
-/// [1] https://github.com/rust-lang/rust/issues/42763
-/// [2] https://github.com/rust-lang/rust/issues/13707
-type SharingCache<E> = SharingCacheBase<StyleSharingCandidate<E>>;
-type TypelessSharingCache = SharingCacheBase<FakeCandidate>;
-type StoredSharingCache = Arc<AtomicRefCell<TypelessSharingCache>>;
-
-thread_local! {
- // See the comment on bloom.rs about why do we leak this.
- static SHARING_CACHE_KEY: ManuallyDrop<StoredSharingCache> =
- ManuallyDrop::new(Arc::new_leaked(Default::default()));
-}
-
-/// An LRU cache of the last few nodes seen, so that we can aggressively try to
-/// reuse their styles.
-///
-/// Note that this cache is flushed every time we steal work from the queue, so
-/// storing nodes here temporarily is safe.
-pub struct StyleSharingCache<E: TElement> {
- /// The LRU cache, with the type cast away to allow persisting the allocation.
- cache_typeless: OwningHandle<StoredSharingCache, AtomicRefMut<'static, TypelessSharingCache>>,
- /// Bind this structure to the lifetime of E, since that's what we effectively store.
- marker: PhantomData<SendElement<E>>,
- /// The DOM depth we're currently at. This is used as an optimization to
- /// clear the cache when we change depths, since we know at that point
- /// nothing in the cache will match.
- dom_depth: usize,
-}
-
-impl<E: TElement> Drop for StyleSharingCache<E> {
- fn drop(&mut self) {
- self.clear();
- }
-}
-
-impl<E: TElement> StyleSharingCache<E> {
- #[allow(dead_code)]
- fn cache(&self) -> &SharingCache<E> {
- let base: &TypelessSharingCache = &*self.cache_typeless;
- unsafe { mem::transmute(base) }
- }
-
- fn cache_mut(&mut self) -> &mut SharingCache<E> {
- let base: &mut TypelessSharingCache = &mut *self.cache_typeless;
- unsafe { mem::transmute(base) }
- }
-
- /// Create a new style sharing candidate cache.
-
- // Forced out of line to limit stack frame sizes after extra inlining from
- // https://github.com/rust-lang/rust/pull/43931
- //
- // See https://github.com/servo/servo/pull/18420#issuecomment-328769322
- #[inline(never)]
- pub fn new() -> Self {
- assert_eq!(
- mem::size_of::<SharingCache<E>>(),
- mem::size_of::<TypelessSharingCache>()
- );
- assert_eq!(
- mem::align_of::<SharingCache<E>>(),
- mem::align_of::<TypelessSharingCache>()
- );
- let cache_arc = SHARING_CACHE_KEY.with(|c| Arc::clone(&*c));
- let cache =
- OwningHandle::new_with_fn(cache_arc, |x| unsafe { x.as_ref() }.unwrap().borrow_mut());
- debug_assert!(cache.is_empty());
-
- StyleSharingCache {
- cache_typeless: cache,
- marker: PhantomData,
- dom_depth: 0,
- }
- }
-
- /// Tries to insert an element in the style sharing cache.
- ///
- /// Fails if we know it should never be in the cache.
- ///
- /// NB: We pass a source for the validation data, rather than the data itself,
- /// to avoid memmoving at each function call. See rust issue #42763.
- pub fn insert_if_possible(
- &mut self,
- element: &E,
- style: &PrimaryStyle,
- validation_data_holder: Option<&mut StyleSharingTarget<E>>,
- dom_depth: usize,
- shared_context: &SharedStyleContext,
- ) {
- let parent = match element.traversal_parent() {
- Some(element) => element,
- None => {
- debug!("Failing to insert to the cache: no parent element");
- return;
- },
- };
-
- if !element.matches_user_and_content_rules() {
- debug!("Failing to insert into the cache: no tree rules:");
- return;
- }
-
- // We can't share style across shadow hosts right now, because they may
- // match different :host rules.
- //
- // TODO(emilio): We could share across the ones that don't have :host
- // rules or have the same.
- if element.shadow_root().is_some() {
- debug!("Failing to insert into the cache: Shadow Host");
- return;
- }
-
- // If the element has running animations, we can't share style.
- //
- // This is distinct from the specifies_{animations,transitions} check below,
- // because:
- // * Animations can be triggered directly via the Web Animations API.
- // * Our computed style can still be affected by animations after we no
- // longer match any animation rules, since removing animations involves
- // a sequential task and an additional traversal.
- if element.has_animations(shared_context) {
- debug!("Failing to insert to the cache: running animations");
- return;
- }
-
- // If this element was considered for matching a relative selector, we can't
- // share style, as that requires evaluating the relative selector in the
- // first place. A couple notes:
- // - This means that a document that contains a standalone `:has()`
- // rule would basically turn style sharing off.
- // - Since the flag is set on the element, we may be overly pessimistic:
- // For example, given `<div class="foo"><div class="bar"></div></div>`,
- // if we run into a `.foo:has(.bar) .baz` selector, we refuse any selector
- // matching `.foo`, even if `:has()` may not even be used. Ideally we'd
- // have something like `RelativeSelectorConsidered::RightMost`, but the
- // element flag is required for invalidation, and this reduces more tracking.
- if style
- .style
- .0
- .flags
- .intersects(ComputedValueFlags::ANCHORS_RELATIVE_SELECTOR) {
- debug!("Failing to insert to the cache: may anchor relative selector");
- return;
- }
-
- // In addition to the above running animations check, we also need to
- // check CSS animation and transition styles since it's possible that
- // we are about to create CSS animations/transitions.
- //
- // These are things we don't check in the candidate match because they
- // are either uncommon or expensive.
- let ui_style = style.style().get_ui();
- if ui_style.specifies_transitions() {
- debug!("Failing to insert to the cache: transitions");
- return;
- }
-
- if ui_style.specifies_animations() {
- debug!("Failing to insert to the cache: animations");
- return;
- }
-
- debug!(
- "Inserting into cache: {:?} with parent {:?}",
- element, parent
- );
-
- if self.dom_depth != dom_depth {
- debug!(
- "Clearing cache because depth changed from {:?} to {:?}, element: {:?}",
- self.dom_depth, dom_depth, element
- );
- self.clear();
- self.dom_depth = dom_depth;
- }
- self.cache_mut().insert(
- *element,
- style
- .style
- .0
- .flags
- .intersects(ComputedValueFlags::CONSIDERED_RELATIVE_SELECTOR),
- validation_data_holder,
- );
- }
-
- /// Clear the style sharing candidate cache.
- pub fn clear(&mut self) {
- self.cache_mut().clear();
- }
-
- /// Attempts to share a style with another node.
- fn share_style_if_possible(
- &mut self,
- shared_context: &SharedStyleContext,
- bloom_filter: &StyleBloom<E>,
- nth_index_cache: &mut NthIndexCache,
- target: &mut StyleSharingTarget<E>,
- ) -> Option<ResolvedElementStyles> {
- if shared_context.options.disable_style_sharing_cache {
- debug!(
- "{:?} Cannot share style: style sharing cache disabled",
- target.element
- );
- return None;
- }
-
- if target.inheritance_parent().is_none() {
- debug!(
- "{:?} Cannot share style: element has no parent",
- target.element
- );
- return None;
- }
-
- if !target.matches_user_and_content_rules() {
- debug!("{:?} Cannot share style: content rules", target.element);
- return None;
- }
-
- self.cache_mut().entries.lookup(|candidate| {
- Self::test_candidate(
- target,
- candidate,
- &shared_context,
- bloom_filter,
- nth_index_cache,
- shared_context,
- )
- })
- }
-
- fn test_candidate(
- target: &mut StyleSharingTarget<E>,
- candidate: &mut StyleSharingCandidate<E>,
- shared: &SharedStyleContext,
- bloom: &StyleBloom<E>,
- nth_index_cache: &mut NthIndexCache,
- shared_context: &SharedStyleContext,
- ) -> Option<ResolvedElementStyles> {
- debug_assert!(target.matches_user_and_content_rules());
-
- // Check that we have the same parent, or at least that the parents
- // share styles and permit sharing across their children. The latter
- // check allows us to share style between cousins if the parents
- // shared style.
- if !checks::parents_allow_sharing(target, candidate) {
- trace!("Miss: Parent");
- return None;
- }
-
- if target.local_name() != candidate.element.local_name() {
- trace!("Miss: Local Name");
- return None;
- }
-
- if target.namespace() != candidate.element.namespace() {
- trace!("Miss: Namespace");
- return None;
- }
-
- // We do not ignore visited state here, because Gecko needs to store
- // extra bits on visited styles, so these contexts cannot be shared.
- if target.element.state() != candidate.state() {
- trace!("Miss: User and Author State");
- return None;
- }
-
- if target.is_link() != candidate.element.is_link() {
- trace!("Miss: Link");
- return None;
- }
-
- // If two elements belong to different shadow trees, different rules may
- // apply to them, from the respective trees.
- if target.element.containing_shadow() != candidate.element.containing_shadow() {
- trace!("Miss: Different containing shadow roots");
- return None;
- }
-
- // If the elements are not assigned to the same slot they could match
- // different ::slotted() rules in the slot scope.
- //
- // If two elements are assigned to different slots, even within the same
- // shadow root, they could match different rules, due to the slot being
- // assigned to yet another slot in another shadow root.
- if target.element.assigned_slot() != candidate.element.assigned_slot() {
- // TODO(emilio): We could have a look at whether the shadow roots
- // actually have slotted rules and such.
- trace!("Miss: Different assigned slots");
- return None;
- }
-
- if target.element.shadow_root().is_some() {
- trace!("Miss: Shadow host");
- return None;
- }
-
- if target.element.has_animations(shared_context) {
- trace!("Miss: Has Animations");
- return None;
- }
-
- if target.matches_user_and_content_rules() !=
- candidate.element.matches_user_and_content_rules()
- {
- trace!("Miss: User and Author Rules");
- return None;
- }
-
- // It's possible that there are no styles for either id.
- if checks::may_match_different_id_rules(shared, target.element, candidate.element) {
- trace!("Miss: ID Attr");
- return None;
- }
-
- if !checks::have_same_style_attribute(target, candidate) {
- trace!("Miss: Style Attr");
- return None;
- }
-
- if !checks::have_same_class(target, candidate) {
- trace!("Miss: Class");
- return None;
- }
-
- if !checks::have_same_presentational_hints(target, candidate) {
- trace!("Miss: Pres Hints");
- return None;
- }
-
- if !checks::have_same_parts(target, candidate) {
- trace!("Miss: Shadow parts");
- return None;
- }
-
- if !checks::revalidate(target, candidate, shared, bloom, nth_index_cache) {
- trace!("Miss: Revalidation");
- return None;
- }
-
- debug!(
- "Sharing allowed between {:?} and {:?}",
- target.element, candidate.element
- );
- Some(candidate.element.borrow_data().unwrap().share_styles())
- }
-
- /// Attempts to find an element in the cache with the given primary rule
- /// node and parent.
- ///
- /// FIXME(emilio): re-measure this optimization, and remove if it's not very
- /// useful... It's probably not worth the complexity / obscure bugs.
- pub fn lookup_by_rules(
- &mut self,
- shared_context: &SharedStyleContext,
- inherited: &ComputedValues,
- rules: &StrongRuleNode,
- visited_rules: Option<&StrongRuleNode>,
- target: E,
- ) -> Option<PrimaryStyle> {
- if shared_context.options.disable_style_sharing_cache {
- return None;
- }
-
- self.cache_mut().entries.lookup(|candidate| {
- debug_assert_ne!(candidate.element, target);
- if !candidate.parent_style_identity().eq(inherited) {
- return None;
- }
- let data = candidate.element.borrow_data().unwrap();
- let style = data.styles.primary();
- if style.rules.as_ref() != Some(&rules) {
- return None;
- }
- if style.visited_rules() != visited_rules {
- return None;
- }
- // NOTE(emilio): We only need to check name / namespace because we
- // do name-dependent style adjustments, like the display: contents
- // to display: none adjustment.
- if target.namespace() != candidate.element.namespace() ||
- target.local_name() != candidate.element.local_name()
- {
- return None;
- }
- // When using container units, inherited style + rules matched aren't enough to
- // determine whether the style is the same. We could actually do a full container
- // lookup but for now we just check that our actual traversal parent matches.
- if data
- .styles
- .primary()
- .flags
- .intersects(ComputedValueFlags::USES_CONTAINER_UNITS) &&
- candidate.element.traversal_parent() != target.traversal_parent()
- {
- return None;
- }
- // Rule nodes and styles are computed independent of the element's actual visitedness,
- // but at the end of the cascade (in `adjust_for_visited`) we do store the
- // RELEVANT_LINK_VISITED flag, so we can't share by rule node between visited and
- // unvisited styles. We don't check for visitedness and just refuse to share for links
- // entirely, so that visitedness doesn't affect timing.
- debug_assert_eq!(target.is_link(), candidate.element.is_link(), "Linkness mismatch");
- if target.is_link() {
- return None;
- }
-
- Some(data.share_primary_style())
- })
- }
-}
diff --git a/components/style/str.rs b/components/style/str.rs
deleted file mode 100644
index c610b5241a7..00000000000
--- a/components/style/str.rs
+++ /dev/null
@@ -1,189 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
-
-//! String utils for attributes and similar stuff.
-
-#![deny(missing_docs)]
-
-use num_traits::ToPrimitive;
-use std::borrow::Cow;
-use std::convert::AsRef;
-use std::iter::{Filter, Peekable};
-use std::str::Split;
-
-/// A static slice of characters.
-pub type StaticCharVec = &'static [char];
-
-/// A static slice of `str`s.
-pub type StaticStringVec = &'static [&'static str];
-
-/// A "space character" according to:
-///
-/// <https://html.spec.whatwg.org/multipage/#space-character>
-pub static HTML_SPACE_CHARACTERS: StaticCharVec =
- &['\u{0020}', '\u{0009}', '\u{000a}', '\u{000c}', '\u{000d}'];
-
-/// Whether a character is a HTML whitespace character.
-#[inline]
-pub fn char_is_whitespace(c: char) -> bool {
- HTML_SPACE_CHARACTERS.contains(&c)
-}
-
-/// Whether all the string is HTML whitespace.
-#[inline]
-pub fn is_whitespace(s: &str) -> bool {
- s.chars().all(char_is_whitespace)
-}
-
-#[inline]
-fn not_empty(&split: &&str) -> bool {
- !split.is_empty()
-}
-
-/// Split a string on HTML whitespace.
-#[inline]
-pub fn split_html_space_chars<'a>(
- s: &'a str,
-) -> Filter<Split<'a, StaticCharVec>, fn(&&str) -> bool> {
- s.split(HTML_SPACE_CHARACTERS)
- .filter(not_empty as fn(&&str) -> bool)
-}
-
-/// Split a string on commas.
-#[inline]
-pub fn split_commas<'a>(s: &'a str) -> Filter<Split<'a, char>, fn(&&str) -> bool> {
- s.split(',').filter(not_empty as fn(&&str) -> bool)
-}
-
-/// Character is ascii digit
-pub fn is_ascii_digit(c: &char) -> bool {
- match *c {
- '0'..='9' => true,
- _ => false,
- }
-}
-
-fn is_decimal_point(c: char) -> bool {
- c == '.'
-}
-
-fn is_exponent_char(c: char) -> bool {
- match c {
- 'e' | 'E' => true,
- _ => false,
- }
-}
-
-/// Read a set of ascii digits and read them into a number.
-pub fn read_numbers<I: Iterator<Item = char>>(mut iter: Peekable<I>) -> (Option<i64>, usize) {
- match iter.peek() {
- Some(c) if is_ascii_digit(c) => (),
- _ => return (None, 0),
- }
-
- iter.take_while(is_ascii_digit)
- .map(|d| d as i64 - '0' as i64)
- .fold((Some(0i64), 0), |accumulator, d| {
- let digits = accumulator
- .0
- .and_then(|accumulator| accumulator.checked_mul(10))
- .and_then(|accumulator| accumulator.checked_add(d));
- (digits, accumulator.1 + 1)
- })
-}
-
-/// Read a decimal fraction.
-pub fn read_fraction<I: Iterator<Item = char>>(
- mut iter: Peekable<I>,
- mut divisor: f64,
- value: f64,
-) -> (f64, usize) {
- match iter.peek() {
- Some(c) if is_decimal_point(*c) => (),
- _ => return (value, 0),
- }
- iter.next();
-
- iter.take_while(is_ascii_digit)
- .map(|d| d as i64 - '0' as i64)
- .fold((value, 1), |accumulator, d| {
- divisor *= 10f64;
- (accumulator.0 + d as f64 / divisor, accumulator.1 + 1)
- })
-}
-
-/// Reads an exponent from an iterator over chars, for example `e100`.
-pub fn read_exponent<I: Iterator<Item = char>>(mut iter: Peekable<I>) -> Option<i32> {
- match iter.peek() {
- Some(c) if is_exponent_char(*c) => (),
- _ => return None,
- }
- iter.next();
-
- match iter.peek() {
- None => None,
- Some(&'-') => {
- iter.next();
- read_numbers(iter).0.map(|exp| -exp.to_i32().unwrap_or(0))
- },
- Some(&'+') => {
- iter.next();
- read_numbers(iter).0.map(|exp| exp.to_i32().unwrap_or(0))
- },
- Some(_) => read_numbers(iter).0.map(|exp| exp.to_i32().unwrap_or(0)),
- }
-}
-
-/// Join a set of strings with a given delimiter `join`.
-pub fn str_join<I, T>(strs: I, join: &str) -> String
-where
- I: IntoIterator<Item = T>,
- T: AsRef<str>,
-{
- strs.into_iter()
- .enumerate()
- .fold(String::new(), |mut acc, (i, s)| {
- if i > 0 {
- acc.push_str(join);
- }
- acc.push_str(s.as_ref());
- acc
- })
-}
-
-/// Returns true if a given string has a given prefix with case-insensitive match.
-pub fn starts_with_ignore_ascii_case(string: &str, prefix: &str) -> bool {
- string.len() >= prefix.len() &&
- string.as_bytes()[0..prefix.len()].eq_ignore_ascii_case(prefix.as_bytes())
-}
-
-/// Returns an ascii lowercase version of a string, only allocating if needed.
-pub fn string_as_ascii_lowercase<'a>(input: &'a str) -> Cow<'a, str> {
- if input.bytes().any(|c| matches!(c, b'A'..=b'Z')) {
- input.to_ascii_lowercase().into()
- } else {
- // Already ascii lowercase.
- Cow::Borrowed(input)
- }
-}
-
-/// To avoid accidentally instantiating multiple monomorphizations of large
-/// serialization routines, we define explicit concrete types and require
-/// them in those routines. This avoids accidental mixing of String and
-/// nsACString arguments in Gecko, which would cause code size to blow up.
-#[cfg(feature = "gecko")]
-pub type CssStringWriter = ::nsstring::nsACString;
-
-/// String type that coerces to CssStringWriter, used when serialization code
-/// needs to allocate a temporary string.
-#[cfg(feature = "gecko")]
-pub type CssString = ::nsstring::nsCString;
-
-/// String. The comments for the Gecko types explain the need for this abstraction.
-#[cfg(feature = "servo")]
-pub type CssStringWriter = String;
-
-/// String. The comments for the Gecko types explain the need for this abstraction.
-#[cfg(feature = "servo")]
-pub type CssString = String;
diff --git a/components/style/style_adjuster.rs b/components/style/style_adjuster.rs
deleted file mode 100644
index a353577ec44..00000000000
--- a/components/style/style_adjuster.rs
+++ /dev/null
@@ -1,1013 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
-
-//! A struct to encapsulate all the style fixups and flags propagations
-//! a computed style needs in order for it to adhere to the CSS spec.
-
-use crate::computed_value_flags::ComputedValueFlags;
-use crate::dom::TElement;
-#[cfg(feature = "gecko")]
-use crate::properties::longhands::contain::computed_value::T as Contain;
-#[cfg(feature = "gecko")]
-use crate::properties::longhands::container_type::computed_value::T as ContainerType;
-#[cfg(feature = "gecko")]
-use crate::properties::longhands::content_visibility::computed_value::T as ContentVisibility;
-use crate::properties::longhands::display::computed_value::T as Display;
-use crate::properties::longhands::float::computed_value::T as Float;
-use crate::properties::longhands::position::computed_value::T as Position;
-use crate::properties::{self, ComputedValues, StyleBuilder};
-
-/// A struct that implements all the adjustment methods.
-///
-/// NOTE(emilio): If new adjustments are introduced that depend on reset
-/// properties of the parent, you may need tweaking the
-/// `ChildCascadeRequirement` code in `matching.rs`.
-///
-/// NOTE(emilio): Also, if new adjustments are introduced that break the
-/// following invariant:
-///
-/// Given same tag name, namespace, rules and parent style, two elements would
-/// end up with exactly the same style.
-///
-/// Then you need to adjust the lookup_by_rules conditions in the sharing cache.
-pub struct StyleAdjuster<'a, 'b: 'a> {
- style: &'a mut StyleBuilder<'b>,
-}
-
-#[cfg(feature = "gecko")]
-fn is_topmost_svg_svg_element<E>(e: E) -> bool
-where
- E: TElement,
-{
- debug_assert!(e.is_svg_element());
- if e.local_name() != &*atom!("svg") {
- return false;
- }
-
- let parent = match e.traversal_parent() {
- Some(n) => n,
- None => return true,
- };
-
- if !parent.is_svg_element() {
- return true;
- }
-
- parent.local_name() == &*atom!("foreignObject")
-}
-
-// https://drafts.csswg.org/css-display/#unbox
-#[cfg(feature = "gecko")]
-fn is_effective_display_none_for_display_contents<E>(element: E) -> bool
-where
- E: TElement,
-{
- use crate::Atom;
-
- const SPECIAL_HTML_ELEMENTS: [Atom; 16] = [
- atom!("br"),
- atom!("wbr"),
- atom!("meter"),
- atom!("progress"),
- atom!("canvas"),
- atom!("embed"),
- atom!("object"),
- atom!("audio"),
- atom!("iframe"),
- atom!("img"),
- atom!("video"),
- atom!("frame"),
- atom!("frameset"),
- atom!("input"),
- atom!("textarea"),
- atom!("select"),
- ];
-
- // https://drafts.csswg.org/css-display/#unbox-svg
- //
- // There's a note about "Unknown elements", but there's not a good way to
- // know what that means, or to get that information from here, and no other
- // UA implements this either.
- const SPECIAL_SVG_ELEMENTS: [Atom; 6] = [
- atom!("svg"),
- atom!("a"),
- atom!("g"),
- atom!("use"),
- atom!("tspan"),
- atom!("textPath"),
- ];
-
- // https://drafts.csswg.org/css-display/#unbox-html
- if element.is_html_element() {
- let local_name = element.local_name();
- return SPECIAL_HTML_ELEMENTS
- .iter()
- .any(|name| &**name == local_name);
- }
-
- // https://drafts.csswg.org/css-display/#unbox-svg
- if element.is_svg_element() {
- if is_topmost_svg_svg_element(element) {
- return true;
- }
- let local_name = element.local_name();
- return !SPECIAL_SVG_ELEMENTS
- .iter()
- .any(|name| &**name == local_name);
- }
-
- // https://drafts.csswg.org/css-display/#unbox-mathml
- //
- // We always treat XUL as display: none. We don't use display:
- // contents in XUL anyway, so should be fine to be consistent with
- // MathML unless there's a use case for it.
- if element.is_mathml_element() || element.is_xul_element() {
- return true;
- }
-
- false
-}
-
-impl<'a, 'b: 'a> StyleAdjuster<'a, 'b> {
- /// Trivially constructs a new StyleAdjuster.
- #[inline]
- pub fn new(style: &'a mut StyleBuilder<'b>) -> Self {
- StyleAdjuster { style }
- }
-
- /// <https://fullscreen.spec.whatwg.org/#new-stacking-layer>
- ///
- /// Any position value other than 'absolute' and 'fixed' are
- /// computed to 'absolute' if the element is in a top layer.
- ///
- fn adjust_for_top_layer(&mut self) {
- if !self.style.in_top_layer() {
- return;
- }
- if !self.style.is_absolutely_positioned() {
- self.style.mutate_box().set_position(Position::Absolute);
- }
- if self.style.get_box().clone_display().is_contents() {
- self.style.mutate_box().set_display(Display::Block);
- }
- }
-
- /// -webkit-box with line-clamp and vertical orientation gets turned into
- /// flow-root at computed-value time.
- ///
- /// This makes the element not be a flex container, with all that it
- /// implies, but it should be safe. It matches blink, see
- /// https://bugzilla.mozilla.org/show_bug.cgi?id=1786147#c10
- #[cfg(feature = "gecko")]
- fn adjust_for_webkit_line_clamp(&mut self) {
- use crate::properties::longhands::_moz_box_orient::computed_value::T as BoxOrient;
- use crate::values::specified::box_::{DisplayInside, DisplayOutside};
- let box_style = self.style.get_box();
- if box_style.clone__webkit_line_clamp().is_none() {
- return;
- }
- let disp = box_style.clone_display();
- if disp.inside() != DisplayInside::WebkitBox {
- return;
- }
- if self.style.get_xul().clone__moz_box_orient() != BoxOrient::Vertical {
- return;
- }
- let new_display = if disp.outside() == DisplayOutside::Block {
- Display::FlowRoot
- } else {
- debug_assert_eq!(disp.outside(), DisplayOutside::Inline);
- Display::InlineBlock
- };
- self.style
- .mutate_box()
- .set_adjusted_display(new_display, false);
- }
-
- /// CSS 2.1 section 9.7:
- ///
- /// If 'position' has the value 'absolute' or 'fixed', [...] the computed
- /// value of 'float' is 'none'.
- ///
- fn adjust_for_position(&mut self) {
- if self.style.is_absolutely_positioned() && self.style.is_floating() {
- self.style.mutate_box().set_float(Float::None);
- }
- }
-
- /// Whether we should skip any item-based display property blockification on
- /// this element.
- fn skip_item_display_fixup<E>(&self, element: Option<E>) -> bool
- where
- E: TElement,
- {
- if let Some(pseudo) = self.style.pseudo {
- return pseudo.skip_item_display_fixup();
- }
-
- element.map_or(false, |e| e.skip_item_display_fixup())
- }
-
- /// Apply the blockification rules based on the table in CSS 2.2 section 9.7.
- /// <https://drafts.csswg.org/css2/visuren.html#dis-pos-flo>
- /// A ::marker pseudo-element with 'list-style-position:outside' needs to
- /// have its 'display' blockified, unless the ::marker is for an inline
- /// list-item (for which 'list-style-position:outside' behaves as 'inside').
- /// https://drafts.csswg.org/css-lists-3/#list-style-position-property
- fn blockify_if_necessary<E>(&mut self, layout_parent_style: &ComputedValues, element: Option<E>)
- where
- E: TElement,
- {
- let mut blockify = false;
- macro_rules! blockify_if {
- ($if_what:expr) => {
- if !blockify {
- blockify = $if_what;
- }
- };
- }
-
- blockify_if!(self.style.is_root_element);
- if !self.skip_item_display_fixup(element) {
- let parent_display = layout_parent_style.get_box().clone_display();
- blockify_if!(parent_display.is_item_container());
- }
-
- let is_item_or_root = blockify;
-
- blockify_if!(self.style.is_floating());
- blockify_if!(self.style.is_absolutely_positioned());
-
- if !blockify {
- return;
- }
-
- let display = self.style.get_box().clone_display();
- let blockified_display = display.equivalent_block_display(self.style.is_root_element);
- if display != blockified_display {
- self.style
- .mutate_box()
- .set_adjusted_display(blockified_display, is_item_or_root);
- }
- }
-
- /// Compute a few common flags for both text and element's style.
- fn set_bits(&mut self) {
- let box_style = self.style.get_box();
- let display = box_style.clone_display();
-
- if !display.is_contents() {
- if !self
- .style
- .get_text()
- .clone_text_decoration_line()
- .is_empty()
- {
- self.style
- .add_flags(ComputedValueFlags::HAS_TEXT_DECORATION_LINES);
- }
-
- if self.style.get_effects().clone_opacity() == 0. {
- self.style
- .add_flags(ComputedValueFlags::IS_IN_OPACITY_ZERO_SUBTREE);
- }
- }
-
- if self.style.is_pseudo_element() {
- self.style
- .add_flags(ComputedValueFlags::IS_IN_PSEUDO_ELEMENT_SUBTREE);
- }
-
- if self.style.is_root_element {
- self.style
- .add_flags(ComputedValueFlags::IS_ROOT_ELEMENT_STYLE);
- }
-
- #[cfg(feature = "gecko")]
- if box_style
- .clone_contain()
- .contains(SpecifiedValue::STYLE)
- {
- self.style
- .add_flags(ComputedValueFlags::SELF_OR_ANCESTOR_HAS_CONTAIN_STYLE);
- }
-
- if box_style.clone_container_type().is_size_container_type() {
- self.style
- .add_flags(ComputedValueFlags::SELF_OR_ANCESTOR_HAS_SIZE_CONTAINER_TYPE);
- }
-
- if self.style.get_parent_column().is_multicol() {
- self.style.add_flags(ComputedValueFlags::CAN_BE_FRAGMENTED);
- }
- }
-
- /// Adjust the style for text style.
- ///
- /// The adjustments here are a subset of the adjustments generally, because
- /// text only inherits properties.
- ///
- /// Note that this, for Gecko, comes through Servo_ComputedValues_Inherit.
- #[cfg(feature = "gecko")]
- pub fn adjust_for_text(&mut self) {
- debug_assert!(!self.style.is_root_element);
- self.adjust_for_text_combine_upright();
- self.adjust_for_text_in_ruby();
- self.set_bits();
- }
-
- /// Change writing mode of the text frame for text-combine-upright.
- ///
- /// It is safe to look at our own style because we are looking at inherited
- /// properties, and text is just plain inheritance.
- ///
- /// TODO(emilio): we should (Gecko too) revise these adjustments in presence
- /// of display: contents.
- ///
- /// FIXME(emilio): How does this play with logical properties? Doesn't
- /// mutating writing-mode change the potential physical sides chosen?
- #[cfg(feature = "gecko")]
- fn adjust_for_text_combine_upright(&mut self) {
- use crate::computed_values::text_combine_upright::T as TextCombineUpright;
- use crate::computed_values::writing_mode::T as WritingMode;
- use crate::logical_geometry;
-
- let writing_mode = self.style.get_inherited_box().clone_writing_mode();
- let text_combine_upright = self.style.get_inherited_text().clone_text_combine_upright();
-
- if matches!(
- writing_mode,
- WritingMode::VerticalRl | WritingMode::VerticalLr
- ) && text_combine_upright == TextCombineUpright::All
- {
- self.style.add_flags(ComputedValueFlags::IS_TEXT_COMBINED);
- self.style
- .mutate_inherited_box()
- .set_writing_mode(WritingMode::HorizontalTb);
- self.style.writing_mode =
- logical_geometry::WritingMode::new(self.style.get_inherited_box());
- }
- }
-
- /// Unconditionally propagates the line break suppression flag to text, and
- /// additionally it applies it if it is in any ruby box.
- ///
- /// This is necessary because its parent may not itself have the flag set
- /// (e.g. ruby or ruby containers), thus we may not inherit the flag from
- /// them.
- #[cfg(feature = "gecko")]
- fn adjust_for_text_in_ruby(&mut self) {
- let parent_display = self.style.get_parent_box().clone_display();
- if parent_display.is_ruby_type() ||
- self.style
- .get_parent_flags()
- .contains(ComputedValueFlags::SHOULD_SUPPRESS_LINEBREAK)
- {
- self.style
- .add_flags(ComputedValueFlags::SHOULD_SUPPRESS_LINEBREAK);
- }
- }
-
- /// <https://drafts.csswg.org/css-writing-modes-3/#block-flow:>
- ///
- /// If a box has a different writing-mode value than its containing
- /// block:
- ///
- /// - If the box has a specified display of inline, its display
- /// computes to inline-block. [CSS21]
- ///
- /// This matches the adjustment that Gecko does, not exactly following
- /// the spec. See also:
- ///
- /// <https://lists.w3.org/Archives/Public/www-style/2017Mar/0045.html>
- /// <https://github.com/servo/servo/issues/15754>
- fn adjust_for_writing_mode(&mut self, layout_parent_style: &ComputedValues) {
- let our_writing_mode = self.style.get_inherited_box().clone_writing_mode();
- let parent_writing_mode = layout_parent_style.get_inherited_box().clone_writing_mode();
-
- if our_writing_mode != parent_writing_mode &&
- self.style.get_box().clone_display() == Display::Inline
- {
- // TODO(emilio): Figure out if we can just set the adjusted display
- // on Gecko too and unify this code path.
- if cfg!(feature = "servo") {
- self.style
- .mutate_box()
- .set_adjusted_display(Display::InlineBlock, false);
- } else {
- self.style.mutate_box().set_display(Display::InlineBlock);
- }
- }
- }
-
- /// This implements an out-of-date spec. The new spec moves the handling of
- /// this to layout, which Gecko implements but Servo doesn't.
- ///
- /// See https://github.com/servo/servo/issues/15229
- #[cfg(feature = "servo")]
- fn adjust_for_alignment(&mut self, layout_parent_style: &ComputedValues) {
- use crate::computed_values::align_items::T as AlignItems;
- use crate::computed_values::align_self::T as AlignSelf;
-
- if self.style.get_position().clone_align_self() == AlignSelf::Auto &&
- !self.style.is_absolutely_positioned()
- {
- let self_align = match layout_parent_style.get_position().clone_align_items() {
- AlignItems::Stretch => AlignSelf::Stretch,
- AlignItems::Baseline => AlignSelf::Baseline,
- AlignItems::FlexStart => AlignSelf::FlexStart,
- AlignItems::FlexEnd => AlignSelf::FlexEnd,
- AlignItems::Center => AlignSelf::Center,
- };
- self.style.mutate_position().set_align_self(self_align);
- }
- }
-
- /// The initial value of border-*-width may be changed at computed value
- /// time.
- ///
- /// This is moved to properties.rs for convenience.
- fn adjust_for_border_width(&mut self) {
- properties::adjust_border_width(self.style);
- }
-
- /// column-rule-style: none causes a computed column-rule-width of zero
- /// at computed value time.
- #[cfg(feature = "gecko")]
- fn adjust_for_column_rule_width(&mut self) {
- let column_style = self.style.get_column();
- if !column_style.clone_column_rule_style().none_or_hidden() {
- return;
- }
- if !column_style.column_rule_has_nonzero_width() {
- return;
- }
- self.style
- .mutate_column()
- .set_column_rule_width(crate::Zero::zero());
- }
-
- /// outline-style: none causes a computed outline-width of zero at computed
- /// value time.
- fn adjust_for_outline_width(&mut self) {
- let outline = self.style.get_outline();
- if !outline.clone_outline_style().none_or_hidden() {
- return;
- }
- if !outline.outline_has_nonzero_width() {
- return;
- }
- self.style
- .mutate_outline()
- .set_outline_width(crate::Zero::zero());
- }
-
- /// CSS overflow-x and overflow-y require some fixup as well in some cases.
- /// https://drafts.csswg.org/css-overflow-3/#overflow-properties
- /// "Computed value: as specified, except with `visible`/`clip` computing to
- /// `auto`/`hidden` (respectively) if one of `overflow-x` or `overflow-y` is
- /// neither `visible` nor `clip`."
- fn adjust_for_overflow(&mut self) {
- let overflow_x = self.style.get_box().clone_overflow_x();
- let overflow_y = self.style.get_box().clone_overflow_y();
- if overflow_x == overflow_y {
- return; // optimization for the common case
- }
-
- if overflow_x.is_scrollable() != overflow_y.is_scrollable() {
- let box_style = self.style.mutate_box();
- box_style.set_overflow_x(overflow_x.to_scrollable());
- box_style.set_overflow_y(overflow_y.to_scrollable());
- }
- }
-
- #[cfg(feature = "gecko")]
- fn adjust_for_contain(&mut self) {
- let box_style = self.style.get_box();
- debug_assert_eq!(
- box_style.clone_contain(),
- box_style.clone_effective_containment()
- );
- let container_type = box_style.clone_container_type();
- let content_visibility = box_style.clone_content_visibility();
- if container_type == ContainerType::Normal &&
- content_visibility == ContentVisibility::Visible
- {
- return;
- }
- let old_contain = box_style.clone_contain();
- let mut new_contain = old_contain;
- match content_visibility {
- ContentVisibility::Visible => {},
- // `content-visibility:auto` also applies size containment when content
- // is not relevant (and therefore skipped). This is checked in
- // nsIFrame::GetContainSizeAxes.
- ContentVisibility::Auto => {
- new_contain.insert(Contain::LAYOUT | Contain::PAINT | Contain::STYLE)
- },
- ContentVisibility::Hidden => new_contain
- .insert(Contain::LAYOUT | Contain::PAINT | Contain::SIZE | Contain::STYLE),
- }
- match container_type {
- ContainerType::Normal => {},
- // https://drafts.csswg.org/css-contain-3/#valdef-container-type-inline-size:
- // Applies layout containment, style containment, and inline-size
- // containment to the principal box.
- ContainerType::InlineSize => {
- new_contain.insert(Contain::LAYOUT | Contain::STYLE | Contain::INLINE_SIZE)
- },
- // https://drafts.csswg.org/css-contain-3/#valdef-container-type-size:
- // Applies layout containment, style containment, and size
- // containment to the principal box.
- ContainerType::Size => {
- new_contain.insert(Contain::LAYOUT | Contain::STYLE | Contain::SIZE)
- },
- }
- if new_contain == old_contain {
- return;
- }
- self.style
- .mutate_box()
- .set_effective_containment(new_contain);
- }
-
- /// content-visibility: auto should force contain-intrinsic-size to gain
- /// an auto value
- ///
- /// <https://github.com/w3c/csswg-drafts/issues/8407>
- #[cfg(feature = "gecko")]
- fn adjust_for_contain_intrinsic_size(&mut self) {
- let content_visibility = self.style.get_box().clone_content_visibility();
- if content_visibility != ContentVisibility::Auto {
- return;
- }
-
- let pos = self.style.get_position();
- let new_width = pos.clone_contain_intrinsic_width().add_auto_if_needed();
- let new_height = pos.clone_contain_intrinsic_height().add_auto_if_needed();
- if new_width.is_none() && new_height.is_none() {
- return;
- }
-
- let pos = self.style.mutate_position();
- if let Some(width) = new_width {
- pos.set_contain_intrinsic_width(width);
- }
- if let Some(height) = new_height {
- pos.set_contain_intrinsic_height(height);
- }
- }
-
- /// Handles the relevant sections in:
- ///
- /// https://drafts.csswg.org/css-display/#unbox-html
- ///
- /// And forbidding display: contents in pseudo-elements, at least for now.
- #[cfg(feature = "gecko")]
- fn adjust_for_prohibited_display_contents<E>(&mut self, element: Option<E>)
- where
- E: TElement,
- {
- if self.style.get_box().clone_display() != Display::Contents {
- return;
- }
-
- // FIXME(emilio): ::before and ::after should support display: contents,
- // see bug 1418138.
- if self.style.pseudo.is_some() {
- self.style.mutate_box().set_display(Display::Inline);
- return;
- }
-
- let element = match element {
- Some(e) => e,
- None => return,
- };
-
- if is_effective_display_none_for_display_contents(element) {
- self.style.mutate_box().set_display(Display::None);
- }
- }
-
- /// <textarea>'s editor root needs to inherit the overflow value from its
- /// parent, but we need to make sure it's still scrollable.
- #[cfg(feature = "gecko")]
- fn adjust_for_text_control_editing_root(&mut self) {
- use crate::properties::longhands::overflow_x::computed_value::T as Overflow;
- use crate::selector_parser::PseudoElement;
-
- if self.style.pseudo != Some(&PseudoElement::MozTextControlEditingRoot) {
- return;
- }
-
- let box_style = self.style.get_box();
- let overflow_x = box_style.clone_overflow_x();
- let overflow_y = box_style.clone_overflow_y();
-
- // If at least one is scrollable we'll adjust the other one in
- // adjust_for_overflow if needed.
- if overflow_x.is_scrollable() || overflow_y.is_scrollable() {
- return;
- }
-
- let box_style = self.style.mutate_box();
- box_style.set_overflow_x(Overflow::Auto);
- box_style.set_overflow_y(Overflow::Auto);
- }
-
- /// If a <fieldset> has grid/flex display type, we need to inherit
- /// this type into its ::-moz-fieldset-content anonymous box.
- ///
- /// NOTE(emilio): We don't need to handle the display change for this case
- /// in matching.rs because anonymous box restyling works separately to the
- /// normal cascading process.
- #[cfg(feature = "gecko")]
- fn adjust_for_fieldset_content(&mut self, layout_parent_style: &ComputedValues) {
- use crate::selector_parser::PseudoElement;
-
- if self.style.pseudo != Some(&PseudoElement::FieldsetContent) {
- return;
- }
-
- debug_assert_eq!(self.style.get_box().clone_display(), Display::Block);
- // TODO We actually want style from parent rather than layout
- // parent, so that this fixup doesn't happen incorrectly when
- // when <fieldset> has "display: contents".
- let parent_display = layout_parent_style.get_box().clone_display();
- let new_display = match parent_display {
- Display::Flex | Display::InlineFlex => Some(Display::Flex),
- Display::Grid | Display::InlineGrid => Some(Display::Grid),
- _ => None,
- };
- if let Some(new_display) = new_display {
- self.style.mutate_box().set_display(new_display);
- }
- }
-
- /// -moz-center, -moz-left and -moz-right are used for HTML's alignment.
- ///
- /// This is covering the <div align="right"><table>...</table></div> case.
- ///
- /// In this case, we don't want to inherit the text alignment into the
- /// table.
- #[cfg(feature = "gecko")]
- fn adjust_for_table_text_align(&mut self) {
- use crate::properties::longhands::text_align::computed_value::T as TextAlign;
- if self.style.get_box().clone_display() != Display::Table {
- return;
- }
-
- match self.style.get_inherited_text().clone_text_align() {
- TextAlign::MozLeft | TextAlign::MozCenter | TextAlign::MozRight => {},
- _ => return,
- }
-
- self.style
- .mutate_inherited_text()
- .set_text_align(TextAlign::Start)
- }
-
- /// Computes the used text decoration for Servo.
- ///
- /// FIXME(emilio): This is a layout tree concept, should move away from
- /// style, since otherwise we're going to have the same subtle bugs WebKit
- /// and Blink have with this very same thing.
- #[cfg(feature = "servo")]
- fn adjust_for_text_decorations_in_effect(&mut self) {
- use crate::values::computed::text::TextDecorationsInEffect;
-
- let decorations_in_effect = TextDecorationsInEffect::from_style(&self.style);
- if self.style.get_inherited_text().text_decorations_in_effect != decorations_in_effect {
- self.style
- .mutate_inherited_text()
- .text_decorations_in_effect = decorations_in_effect;
- }
- }
-
- #[cfg(feature = "gecko")]
- fn should_suppress_linebreak<E>(
- &self,
- layout_parent_style: &ComputedValues,
- element: Option<E>,
- ) -> bool
- where
- E: TElement,
- {
- // Line break suppression should only be propagated to in-flow children.
- if self.style.is_floating() || self.style.is_absolutely_positioned() {
- return false;
- }
- let parent_display = layout_parent_style.get_box().clone_display();
- if layout_parent_style
- .flags
- .contains(ComputedValueFlags::SHOULD_SUPPRESS_LINEBREAK)
- {
- // Line break suppression is propagated to any children of
- // line participants.
- if parent_display.is_line_participant() {
- return true;
- }
- }
- match self.style.get_box().clone_display() {
- // Ruby base and text are always non-breakable.
- Display::RubyBase | Display::RubyText => true,
- // Ruby base container and text container are breakable.
- // Non-HTML elements may not form ruby base / text container because
- // they may not respect ruby-internal display values, so we can't
- // make them escaped from line break suppression.
- // Note that, when certain HTML tags, e.g. form controls, have ruby
- // level container display type, they could also escape from the
- // line break suppression flag while they shouldn't. However, it is
- // generally fine as far as they can't break the line inside them.
- Display::RubyBaseContainer | Display::RubyTextContainer
- if element.map_or(true, |e| e.is_html_element()) =>
- {
- false
- },
- // Anything else is non-breakable if and only if its layout parent
- // has a ruby display type, because any of the ruby boxes can be
- // anonymous.
- _ => parent_display.is_ruby_type(),
- }
- }
-
- /// Do ruby-related style adjustments, which include:
- /// * propagate the line break suppression flag,
- /// * inlinify block descendants,
- /// * suppress border and padding for ruby level containers,
- /// * correct unicode-bidi.
- #[cfg(feature = "gecko")]
- fn adjust_for_ruby<E>(&mut self, layout_parent_style: &ComputedValues, element: Option<E>)
- where
- E: TElement,
- {
- use crate::properties::longhands::unicode_bidi::computed_value::T as UnicodeBidi;
-
- let self_display = self.style.get_box().clone_display();
- // Check whether line break should be suppressed for this element.
- if self.should_suppress_linebreak(layout_parent_style, element) {
- self.style
- .add_flags(ComputedValueFlags::SHOULD_SUPPRESS_LINEBREAK);
- // Inlinify the display type if allowed.
- if !self.skip_item_display_fixup(element) {
- let inline_display = self_display.inlinify();
- if self_display != inline_display {
- self.style
- .mutate_box()
- .set_adjusted_display(inline_display, false);
- }
- }
- }
- // Suppress border and padding for ruby level containers.
- // This is actually not part of the spec. It is currently unspecified
- // how border and padding should be handled for ruby level container,
- // and suppressing them here make it easier for layout to handle.
- if self_display.is_ruby_level_container() {
- self.style.reset_border_struct();
- self.style.reset_padding_struct();
- }
-
- // Force bidi isolation on all internal ruby boxes and ruby container
- // per spec https://drafts.csswg.org/css-ruby-1/#bidi
- if self_display.is_ruby_type() {
- let new_value = match self.style.get_text().clone_unicode_bidi() {
- UnicodeBidi::Normal | UnicodeBidi::Embed => Some(UnicodeBidi::Isolate),
- UnicodeBidi::BidiOverride => Some(UnicodeBidi::IsolateOverride),
- _ => None,
- };
- if let Some(new_value) = new_value {
- self.style.mutate_text().set_unicode_bidi(new_value);
- }
- }
- }
-
- /// Computes the RELEVANT_LINK_VISITED flag based on the parent style and on
- /// whether we're a relevant link.
- ///
- /// NOTE(emilio): We don't do this for text styles, which is... dubious, but
- /// Gecko doesn't seem to do it either. It's extremely easy to do if needed
- /// though.
- ///
- /// FIXME(emilio): This isn't technically a style adjustment thingie, could
- /// it move somewhere else?
- fn adjust_for_visited<E>(&mut self, element: Option<E>)
- where
- E: TElement,
- {
- if !self.style.has_visited_style() {
- return;
- }
-
- let is_link_element = self.style.pseudo.is_none() && element.map_or(false, |e| e.is_link());
-
- if !is_link_element {
- return;
- }
-
- if element.unwrap().is_visited_link() {
- self.style
- .add_flags(ComputedValueFlags::IS_RELEVANT_LINK_VISITED);
- } else {
- // Need to remove to handle unvisited link inside visited.
- self.style
- .remove_flags(ComputedValueFlags::IS_RELEVANT_LINK_VISITED);
- }
- }
-
- /// Resolves "justify-items: legacy" based on the inherited style if needed
- /// to comply with:
- ///
- /// <https://drafts.csswg.org/css-align/#valdef-justify-items-legacy>
- #[cfg(feature = "gecko")]
- fn adjust_for_justify_items(&mut self) {
- use crate::values::specified::align;
- let justify_items = self.style.get_position().clone_justify_items();
- if justify_items.specified.0 != align::AlignFlags::LEGACY {
- return;
- }
-
- let parent_justify_items = self.style.get_parent_position().clone_justify_items();
-
- if !parent_justify_items
- .computed
- .0
- .contains(align::AlignFlags::LEGACY)
- {
- return;
- }
-
- if parent_justify_items.computed == justify_items.computed {
- return;
- }
-
- self.style
- .mutate_position()
- .set_computed_justify_items(parent_justify_items.computed);
- }
-
- /// If '-webkit-appearance' is 'menulist' on a <select> element then
- /// the computed value of 'line-height' is 'normal'.
- ///
- /// https://github.com/w3c/csswg-drafts/issues/3257
- #[cfg(feature = "gecko")]
- fn adjust_for_appearance<E>(&mut self, element: Option<E>)
- where
- E: TElement,
- {
- use crate::properties::longhands::appearance::computed_value::T as Appearance;
- use crate::properties::longhands::line_height::computed_value::T as LineHeight;
-
- let box_ = self.style.get_box();
- let appearance = match box_.clone_appearance() {
- Appearance::Auto => box_.clone__moz_default_appearance(),
- a => a,
- };
-
- if appearance == Appearance::Menulist {
- if self.style.get_inherited_text().clone_line_height() == LineHeight::normal() {
- return;
- }
- if self.style.pseudo.is_some() {
- return;
- }
- let is_html_select_element = element.map_or(false, |e| {
- e.is_html_element() && e.local_name() == &*atom!("select")
- });
- if !is_html_select_element {
- return;
- }
- self.style
- .mutate_inherited_text()
- .set_line_height(LineHeight::normal());
- }
- }
-
- /// A legacy ::marker (i.e. no 'content') without an author-specified 'font-family'
- /// and 'list-style-type:disc|circle|square|disclosure-closed|disclosure-open'
- /// is assigned 'font-family:-moz-bullet-font'. (This is for <ul><li> etc.)
- /// We don't want synthesized italic/bold for this font, so turn that off too.
- /// Likewise for 'letter/word-spacing' -- unless the author specified it then reset
- /// them to their initial value because traditionally we never added such spacing
- /// between a legacy bullet and the list item's content, so we keep that behavior
- /// for web-compat reasons.
- /// We intentionally don't check 'list-style-image' below since we want it to use
- /// the same font as its fallback ('list-style-type') in case it fails to load.
- #[cfg(feature = "gecko")]
- fn adjust_for_marker_pseudo(&mut self) {
- use crate::values::computed::counters::Content;
- use crate::values::computed::font::{FontFamily, FontSynthesis};
- use crate::values::computed::text::{LetterSpacing, WordSpacing};
-
- let is_legacy_marker = self.style.pseudo.map_or(false, |p| p.is_marker()) &&
- self.style.get_list().clone_list_style_type().is_bullet() &&
- self.style.get_counters().clone_content() == Content::Normal;
- if !is_legacy_marker {
- return;
- }
- let flags = self.style.flags.get();
- if !flags.contains(ComputedValueFlags::HAS_AUTHOR_SPECIFIED_FONT_FAMILY) {
- self.style
- .mutate_font()
- .set_font_family(FontFamily::moz_bullet().clone());
-
- // FIXME(mats): We can remove this if support for font-synthesis is added to @font-face rules.
- // Then we can add it to the @font-face rule in html.css instead.
- // https://github.com/w3c/csswg-drafts/issues/6081
- if !flags.contains(ComputedValueFlags::HAS_AUTHOR_SPECIFIED_FONT_SYNTHESIS_WEIGHT) {
- self.style
- .mutate_font()
- .set_font_synthesis_weight(FontSynthesis::None);
- }
- if !flags.contains(ComputedValueFlags::HAS_AUTHOR_SPECIFIED_FONT_SYNTHESIS_STYLE) {
- self.style
- .mutate_font()
- .set_font_synthesis_style(FontSynthesis::None);
- }
- }
- if !flags.contains(ComputedValueFlags::HAS_AUTHOR_SPECIFIED_LETTER_SPACING) {
- self.style
- .mutate_inherited_text()
- .set_letter_spacing(LetterSpacing::normal());
- }
- if !flags.contains(ComputedValueFlags::HAS_AUTHOR_SPECIFIED_WORD_SPACING) {
- self.style
- .mutate_inherited_text()
- .set_word_spacing(WordSpacing::normal());
- }
- }
-
- /// Adjusts the style to account for various fixups that don't fit naturally
- /// into the cascade.
- ///
- /// When comparing to Gecko, this is similar to the work done by
- /// `ComputedStyle::ApplyStyleFixups`, plus some parts of
- /// `nsStyleSet::GetContext`.
- pub fn adjust<E>(&mut self, layout_parent_style: &ComputedValues, element: Option<E>)
- where
- E: TElement,
- {
- if cfg!(debug_assertions) {
- if element.map_or(false, |e| e.is_pseudo_element()) {
- // It'd be nice to assert `self.style.pseudo == Some(&pseudo)`,
- // but we do resolve ::-moz-list pseudos on ::before / ::after
- // content, sigh.
- debug_assert!(self.style.pseudo.is_some(), "Someone really messed up");
- }
- }
- // FIXME(emilio): The apply_declarations callsite in Servo's
- // animation, and the font stuff for Gecko
- // (Stylist::compute_for_declarations) should pass an element to
- // cascade(), then we can make this assertion hold everywhere.
- // debug_assert!(
- // element.is_some() || self.style.pseudo.is_some(),
- // "Should always have an element around for non-pseudo styles"
- // );
-
- self.adjust_for_visited(element);
- #[cfg(feature = "gecko")]
- {
- self.adjust_for_prohibited_display_contents(element);
- self.adjust_for_fieldset_content(layout_parent_style);
- // NOTE: It's important that this happens before
- // adjust_for_overflow.
- self.adjust_for_text_control_editing_root();
- }
- self.adjust_for_top_layer();
- self.blockify_if_necessary(layout_parent_style, element);
- #[cfg(feature = "gecko")]
- self.adjust_for_webkit_line_clamp();
- self.adjust_for_position();
- self.adjust_for_overflow();
- #[cfg(feature = "gecko")]
- {
- self.adjust_for_contain();
- self.adjust_for_contain_intrinsic_size();
- self.adjust_for_table_text_align();
- self.adjust_for_justify_items();
- }
- #[cfg(feature = "servo")]
- {
- self.adjust_for_alignment(layout_parent_style);
- }
- self.adjust_for_border_width();
- #[cfg(feature = "gecko")]
- self.adjust_for_column_rule_width();
- self.adjust_for_outline_width();
- self.adjust_for_writing_mode(layout_parent_style);
- #[cfg(feature = "gecko")]
- {
- self.adjust_for_ruby(layout_parent_style, element);
- }
- #[cfg(feature = "servo")]
- {
- self.adjust_for_text_decorations_in_effect();
- }
- #[cfg(feature = "gecko")]
- {
- self.adjust_for_appearance(element);
- self.adjust_for_marker_pseudo();
- }
- self.set_bits();
- }
-}
diff --git a/components/style/style_resolver.rs b/components/style/style_resolver.rs
deleted file mode 100644
index 7ebcb97eefb..00000000000
--- a/components/style/style_resolver.rs
+++ /dev/null
@@ -1,597 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
-
-//! Style resolution for a given element or pseudo-element.
-
-use crate::applicable_declarations::ApplicableDeclarationList;
-use crate::computed_value_flags::ComputedValueFlags;
-use crate::context::{CascadeInputs, ElementCascadeInputs, StyleContext};
-use crate::data::{EagerPseudoStyles, ElementStyles};
-use crate::dom::TElement;
-use crate::matching::MatchMethods;
-use crate::properties::longhands::display::computed_value::T as Display;
-use crate::properties::ComputedValues;
-use crate::rule_tree::StrongRuleNode;
-use crate::selector_parser::{PseudoElement, SelectorImpl};
-use crate::stylist::RuleInclusion;
-use log::Level::Trace;
-use selectors::matching::{MatchingContext, NeedsSelectorFlags, RelativeSelectorMatchingState};
-use selectors::matching::{MatchingMode, VisitedHandlingMode};
-use servo_arc::Arc;
-
-/// Whether pseudo-elements should be resolved or not.
-#[derive(Clone, Copy, Debug, Eq, PartialEq)]
-pub enum PseudoElementResolution {
- /// Only resolve pseudo-styles if possibly applicable.
- IfApplicable,
- /// Force pseudo-element resolution.
- Force,
-}
-
-/// A struct that takes care of resolving the style of a given element.
-pub struct StyleResolverForElement<'a, 'ctx, 'le, E>
-where
- 'ctx: 'a,
- 'le: 'ctx,
- E: TElement + MatchMethods + 'le,
-{
- element: E,
- context: &'a mut StyleContext<'ctx, E>,
- rule_inclusion: RuleInclusion,
- pseudo_resolution: PseudoElementResolution,
- _marker: ::std::marker::PhantomData<&'le E>,
-}
-
-struct MatchingResults {
- rule_node: StrongRuleNode,
- flags: ComputedValueFlags,
-}
-
-/// A style returned from the resolver machinery.
-pub struct ResolvedStyle(pub Arc<ComputedValues>);
-
-/// The primary style of an element or an element-backed pseudo-element.
-pub struct PrimaryStyle {
- /// The style itself.
- pub style: ResolvedStyle,
- /// Whether the style was reused from another element via the rule node (see
- /// `StyleSharingCache::lookup_by_rules`).
- pub reused_via_rule_node: bool,
-}
-
-/// A set of style returned from the resolver machinery.
-pub struct ResolvedElementStyles {
- /// Primary style.
- pub primary: PrimaryStyle,
- /// Pseudo styles.
- pub pseudos: EagerPseudoStyles,
-}
-
-impl ResolvedElementStyles {
- /// Convenience accessor for the primary style.
- pub fn primary_style(&self) -> &Arc<ComputedValues> {
- &self.primary.style.0
- }
-
- /// Convenience mutable accessor for the style.
- pub fn primary_style_mut(&mut self) -> &mut Arc<ComputedValues> {
- &mut self.primary.style.0
- }
-}
-
-impl PrimaryStyle {
- /// Convenience accessor for the style.
- pub fn style(&self) -> &ComputedValues {
- &*self.style.0
- }
-}
-
-impl From<ResolvedElementStyles> for ElementStyles {
- fn from(r: ResolvedElementStyles) -> ElementStyles {
- ElementStyles {
- primary: Some(r.primary.style.0),
- pseudos: r.pseudos,
- }
- }
-}
-
-fn with_default_parent_styles<E, F, R>(element: E, f: F) -> R
-where
- E: TElement,
- F: FnOnce(Option<&ComputedValues>, Option<&ComputedValues>) -> R,
-{
- let parent_el = element.inheritance_parent();
- let parent_data = parent_el.as_ref().and_then(|e| e.borrow_data());
- let parent_style = parent_data.as_ref().map(|d| d.styles.primary());
-
- let mut layout_parent_el = parent_el.clone();
- let layout_parent_data;
- let mut layout_parent_style = parent_style;
- if parent_style.map_or(false, |s| s.is_display_contents()) {
- layout_parent_el = Some(layout_parent_el.unwrap().layout_parent());
- layout_parent_data = layout_parent_el.as_ref().unwrap().borrow_data().unwrap();
- layout_parent_style = Some(layout_parent_data.styles.primary());
- }
-
- f(
- parent_style.map(|x| &**x),
- layout_parent_style.map(|s| &**s),
- )
-}
-
-fn layout_parent_style_for_pseudo<'a>(
- primary_style: &'a PrimaryStyle,
- layout_parent_style: Option<&'a ComputedValues>,
-) -> Option<&'a ComputedValues> {
- if primary_style.style().is_display_contents() {
- layout_parent_style
- } else {
- Some(primary_style.style())
- }
-}
-
-fn eager_pseudo_is_definitely_not_generated(
- pseudo: &PseudoElement,
- style: &ComputedValues,
-) -> bool {
- if !pseudo.is_before_or_after() {
- return false;
- }
-
- if !style
- .flags
- .intersects(ComputedValueFlags::DISPLAY_DEPENDS_ON_INHERITED_STYLE) &&
- style.get_box().clone_display() == Display::None
- {
- return true;
- }
-
- if !style
- .flags
- .intersects(ComputedValueFlags::CONTENT_DEPENDS_ON_INHERITED_STYLE) &&
- style.ineffective_content_property()
- {
- return true;
- }
-
- false
-}
-
-impl<'a, 'ctx, 'le, E> StyleResolverForElement<'a, 'ctx, 'le, E>
-where
- 'ctx: 'a,
- 'le: 'ctx,
- E: TElement + MatchMethods + 'le,
-{
- /// Trivially construct a new StyleResolverForElement.
- pub fn new(
- element: E,
- context: &'a mut StyleContext<'ctx, E>,
- rule_inclusion: RuleInclusion,
- pseudo_resolution: PseudoElementResolution,
- ) -> Self {
- Self {
- element,
- context,
- rule_inclusion,
- pseudo_resolution,
- _marker: ::std::marker::PhantomData,
- }
- }
-
- /// Resolve just the style of a given element.
- pub fn resolve_primary_style(
- &mut self,
- parent_style: Option<&ComputedValues>,
- layout_parent_style: Option<&ComputedValues>,
- ) -> PrimaryStyle {
- let primary_results = self.match_primary(VisitedHandlingMode::AllLinksUnvisited);
-
- let inside_link = parent_style.map_or(false, |s| s.visited_style().is_some());
-
- let visited_rules = if self.context.shared.visited_styles_enabled &&
- (inside_link || self.element.is_link())
- {
- let visited_matching_results =
- self.match_primary(VisitedHandlingMode::RelevantLinkVisited);
- Some(visited_matching_results.rule_node)
- } else {
- None
- };
-
- self.cascade_primary_style(
- CascadeInputs {
- rules: Some(primary_results.rule_node),
- visited_rules,
- flags: primary_results.flags,
- },
- parent_style,
- layout_parent_style,
- )
- }
-
- fn cascade_primary_style(
- &mut self,
- inputs: CascadeInputs,
- parent_style: Option<&ComputedValues>,
- layout_parent_style: Option<&ComputedValues>,
- ) -> PrimaryStyle {
- // Before doing the cascade, check the sharing cache and see if we can
- // reuse the style via rule node identity.
- let may_reuse = self.element.matches_user_and_content_rules() &&
- parent_style.is_some() &&
- inputs.rules.is_some();
-
- if may_reuse {
- let cached = self.context.thread_local.sharing_cache.lookup_by_rules(
- self.context.shared,
- parent_style.unwrap(),
- inputs.rules.as_ref().unwrap(),
- inputs.visited_rules.as_ref(),
- self.element,
- );
- if let Some(mut primary_style) = cached {
- self.context.thread_local.statistics.styles_reused += 1;
- primary_style.reused_via_rule_node |= true;
- return primary_style;
- }
- }
-
- // No style to reuse. Cascade the style, starting with visited style
- // if necessary.
- PrimaryStyle {
- style: self.cascade_style_and_visited(
- inputs,
- parent_style,
- layout_parent_style,
- /* pseudo = */ None,
- ),
- reused_via_rule_node: false,
- }
- }
-
- /// Resolve the style of a given element, and all its eager pseudo-elements.
- pub fn resolve_style(
- &mut self,
- parent_style: Option<&ComputedValues>,
- layout_parent_style: Option<&ComputedValues>,
- ) -> ResolvedElementStyles {
- let primary_style = self.resolve_primary_style(parent_style, layout_parent_style);
-
- let mut pseudo_styles = EagerPseudoStyles::default();
-
- if !self.element.is_pseudo_element() {
- let layout_parent_style_for_pseudo =
- layout_parent_style_for_pseudo(&primary_style, layout_parent_style);
- SelectorImpl::each_eagerly_cascaded_pseudo_element(|pseudo| {
- let pseudo_style = self.resolve_pseudo_style(
- &pseudo,
- &primary_style,
- layout_parent_style_for_pseudo,
- );
-
- if let Some(style) = pseudo_style {
- if !matches!(self.pseudo_resolution, PseudoElementResolution::Force) &&
- eager_pseudo_is_definitely_not_generated(&pseudo, &style.0)
- {
- return;
- }
- pseudo_styles.set(&pseudo, style.0);
- }
- })
- }
-
- ResolvedElementStyles {
- primary: primary_style,
- pseudos: pseudo_styles,
- }
- }
-
- /// Resolve an element's styles with the default inheritance parent/layout
- /// parents.
- pub fn resolve_style_with_default_parents(&mut self) -> ResolvedElementStyles {
- with_default_parent_styles(self.element, |parent_style, layout_parent_style| {
- self.resolve_style(parent_style, layout_parent_style)
- })
- }
-
- /// Cascade a set of rules, using the default parent for inheritance.
- pub fn cascade_style_and_visited_with_default_parents(
- &mut self,
- inputs: CascadeInputs,
- ) -> ResolvedStyle {
- with_default_parent_styles(self.element, |parent_style, layout_parent_style| {
- self.cascade_style_and_visited(
- inputs,
- parent_style,
- layout_parent_style,
- /* pseudo = */ None,
- )
- })
- }
-
- /// Cascade a set of rules for pseudo element, using the default parent for inheritance.
- pub fn cascade_style_and_visited_for_pseudo_with_default_parents(
- &mut self,
- inputs: CascadeInputs,
- pseudo: &PseudoElement,
- primary_style: &PrimaryStyle,
- ) -> ResolvedStyle {
- with_default_parent_styles(self.element, |_, layout_parent_style| {
- let layout_parent_style_for_pseudo =
- layout_parent_style_for_pseudo(primary_style, layout_parent_style);
-
- self.cascade_style_and_visited(
- inputs,
- Some(primary_style.style()),
- layout_parent_style_for_pseudo,
- Some(pseudo),
- )
- })
- }
-
- fn cascade_style_and_visited(
- &mut self,
- inputs: CascadeInputs,
- parent_style: Option<&ComputedValues>,
- layout_parent_style: Option<&ComputedValues>,
- pseudo: Option<&PseudoElement>,
- ) -> ResolvedStyle {
- debug_assert!(pseudo.map_or(true, |p| p.is_eager()));
-
- let implemented_pseudo = self.element.implemented_pseudo_element();
- let pseudo = pseudo.or(implemented_pseudo.as_ref());
-
- let mut conditions = Default::default();
- let values = self.context.shared.stylist.cascade_style_and_visited(
- Some(self.element),
- pseudo,
- inputs,
- &self.context.shared.guards,
- pseudo.and(parent_style),
- parent_style,
- parent_style,
- layout_parent_style,
- Some(&self.context.thread_local.rule_cache),
- &mut conditions,
- );
-
- self.context.thread_local.rule_cache.insert_if_possible(
- &self.context.shared.guards,
- &values,
- pseudo,
- &conditions,
- );
-
- ResolvedStyle(values)
- }
-
- /// Cascade the element and pseudo-element styles with the default parents.
- pub fn cascade_styles_with_default_parents(
- &mut self,
- inputs: ElementCascadeInputs,
- ) -> ResolvedElementStyles {
- with_default_parent_styles(self.element, move |parent_style, layout_parent_style| {
- let primary_style =
- self.cascade_primary_style(inputs.primary, parent_style, layout_parent_style);
-
- let mut pseudo_styles = EagerPseudoStyles::default();
- if let Some(mut pseudo_array) = inputs.pseudos.into_array() {
- let layout_parent_style_for_pseudo = if primary_style.style().is_display_contents()
- {
- layout_parent_style
- } else {
- Some(primary_style.style())
- };
-
- for (i, inputs) in pseudo_array.iter_mut().enumerate() {
- if let Some(inputs) = inputs.take() {
- let pseudo = PseudoElement::from_eager_index(i);
-
- let style = self.cascade_style_and_visited(
- inputs,
- Some(primary_style.style()),
- layout_parent_style_for_pseudo,
- Some(&pseudo),
- );
-
- if !matches!(self.pseudo_resolution, PseudoElementResolution::Force) &&
- eager_pseudo_is_definitely_not_generated(&pseudo, &style.0)
- {
- continue;
- }
-
- pseudo_styles.set(&pseudo, style.0);
- }
- }
- }
-
- ResolvedElementStyles {
- primary: primary_style,
- pseudos: pseudo_styles,
- }
- })
- }
-
- fn resolve_pseudo_style(
- &mut self,
- pseudo: &PseudoElement,
- originating_element_style: &PrimaryStyle,
- layout_parent_style: Option<&ComputedValues>,
- ) -> Option<ResolvedStyle> {
- let MatchingResults {
- rule_node,
- mut flags,
- } = self.match_pseudo(
- &originating_element_style.style.0,
- pseudo,
- VisitedHandlingMode::AllLinksUnvisited,
- )?;
-
- let mut visited_rules = None;
- if originating_element_style.style().visited_style().is_some() {
- visited_rules = self
- .match_pseudo(
- &originating_element_style.style.0,
- pseudo,
- VisitedHandlingMode::RelevantLinkVisited,
- )
- .map(|results| {
- flags |= results.flags;
- results.rule_node
- });
- }
-
- Some(self.cascade_style_and_visited(
- CascadeInputs {
- rules: Some(rule_node),
- visited_rules,
- flags,
- },
- Some(originating_element_style.style()),
- layout_parent_style,
- Some(pseudo),
- ))
- }
-
- fn match_primary(&mut self, visited_handling: VisitedHandlingMode) -> MatchingResults {
- debug!(
- "Match primary for {:?}, visited: {:?}",
- self.element, visited_handling
- );
- let mut applicable_declarations = ApplicableDeclarationList::new();
-
- let bloom_filter = self.context.thread_local.bloom_filter.filter();
- let nth_index_cache = &mut self.context.thread_local.nth_index_cache;
- let mut matching_context = MatchingContext::new_for_visited(
- MatchingMode::Normal,
- Some(bloom_filter),
- nth_index_cache,
- visited_handling,
- self.context.shared.quirks_mode(),
- NeedsSelectorFlags::Yes,
- );
-
- let stylist = &self.context.shared.stylist;
- let implemented_pseudo = self.element.implemented_pseudo_element();
- // Compute the primary rule node.
- stylist.push_applicable_declarations(
- self.element,
- implemented_pseudo.as_ref(),
- self.element.style_attribute(),
- self.element.smil_override(),
- self.element.animation_declarations(self.context.shared),
- self.rule_inclusion,
- &mut applicable_declarations,
- &mut matching_context,
- );
-
- // FIXME(emilio): This is a hack for animations, and should go away.
- self.element.unset_dirty_style_attribute();
-
- let rule_node = stylist
- .rule_tree()
- .compute_rule_node(&mut applicable_declarations, &self.context.shared.guards);
-
- if log_enabled!(Trace) {
- trace!("Matched rules for {:?}:", self.element);
- for rn in rule_node.self_and_ancestors() {
- let source = rn.style_source();
- if source.is_some() {
- trace!(" > {:?}", source);
- }
- }
- }
- // This is a bit awkward - ideally, the flag is set directly where `considered_relative_selector`
- // is; however, in that context, the implementation detail of `extra_data` is not visible, so
- // it's done here. A trait for manipulating the flags is an option, but not worth it for a single flag.
- match matching_context.considered_relative_selector {
- RelativeSelectorMatchingState::None => (),
- RelativeSelectorMatchingState::Considered => {
- matching_context
- .extra_data
- .cascade_input_flags
- .insert(ComputedValueFlags::CONSIDERED_RELATIVE_SELECTOR);
- },
- RelativeSelectorMatchingState::ConsideredAnchor => {
- matching_context.extra_data.cascade_input_flags.insert(
- ComputedValueFlags::ANCHORS_RELATIVE_SELECTOR |
- ComputedValueFlags::CONSIDERED_RELATIVE_SELECTOR,
- );
- },
- };
-
- MatchingResults {
- rule_node,
- flags: matching_context.extra_data.cascade_input_flags,
- }
- }
-
- fn match_pseudo(
- &mut self,
- originating_element_style: &ComputedValues,
- pseudo_element: &PseudoElement,
- visited_handling: VisitedHandlingMode,
- ) -> Option<MatchingResults> {
- debug!(
- "Match pseudo {:?} for {:?}, visited: {:?}",
- self.element, pseudo_element, visited_handling
- );
- debug_assert!(pseudo_element.is_eager());
- debug_assert!(
- !self.element.is_pseudo_element(),
- "Element pseudos can't have any other eager pseudo."
- );
-
- let mut applicable_declarations = ApplicableDeclarationList::new();
-
- let stylist = &self.context.shared.stylist;
-
- if !self
- .element
- .may_generate_pseudo(pseudo_element, originating_element_style)
- {
- return None;
- }
-
- let bloom_filter = self.context.thread_local.bloom_filter.filter();
- let nth_index_cache = &mut self.context.thread_local.nth_index_cache;
-
- let mut matching_context = MatchingContext::<'_, E::Impl>::new_for_visited(
- MatchingMode::ForStatelessPseudoElement,
- Some(bloom_filter),
- nth_index_cache,
- visited_handling,
- self.context.shared.quirks_mode(),
- NeedsSelectorFlags::Yes,
- );
- matching_context.extra_data.originating_element_style = Some(originating_element_style);
-
- // NB: We handle animation rules for ::before and ::after when
- // traversing them.
- stylist.push_applicable_declarations(
- self.element,
- Some(pseudo_element),
- None,
- None,
- /* animation_declarations = */ Default::default(),
- self.rule_inclusion,
- &mut applicable_declarations,
- &mut matching_context,
- );
-
- if applicable_declarations.is_empty() {
- return None;
- }
-
- let rule_node = stylist
- .rule_tree()
- .compute_rule_node(&mut applicable_declarations, &self.context.shared.guards);
-
- Some(MatchingResults {
- rule_node,
- flags: matching_context.extra_data.cascade_input_flags,
- })
- }
-}
diff --git a/components/style/stylesheet_set.rs b/components/style/stylesheet_set.rs
deleted file mode 100644
index e2937e06e75..00000000000
--- a/components/style/stylesheet_set.rs
+++ /dev/null
@@ -1,705 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
-
-//! A centralized set of stylesheets for a document.
-
-use crate::dom::TElement;
-use crate::invalidation::stylesheets::{RuleChangeKind, StylesheetInvalidationSet};
-use crate::media_queries::Device;
-use crate::selector_parser::SnapshotMap;
-use crate::shared_lock::SharedRwLockReadGuard;
-use crate::stylesheets::{
- CssRule, Origin, OriginSet, OriginSetIterator, PerOrigin, StylesheetInDocument,
-};
-use std::{mem, slice};
-
-/// Entry for a StylesheetSet.
-#[derive(MallocSizeOf)]
-struct StylesheetSetEntry<S>
-where
- S: StylesheetInDocument + PartialEq + 'static,
-{
- /// The sheet.
- sheet: S,
-
- /// Whether this sheet has been part of at least one flush.
- committed: bool,
-}
-
-impl<S> StylesheetSetEntry<S>
-where
- S: StylesheetInDocument + PartialEq + 'static,
-{
- fn new(sheet: S) -> Self {
- Self {
- sheet,
- committed: false,
- }
- }
-}
-
-/// A iterator over the stylesheets of a list of entries in the StylesheetSet.
-pub struct StylesheetCollectionIterator<'a, S>(slice::Iter<'a, StylesheetSetEntry<S>>)
-where
- S: StylesheetInDocument + PartialEq + 'static;
-
-impl<'a, S> Clone for StylesheetCollectionIterator<'a, S>
-where
- S: StylesheetInDocument + PartialEq + 'static,
-{
- fn clone(&self) -> Self {
- StylesheetCollectionIterator(self.0.clone())
- }
-}
-
-impl<'a, S> Iterator for StylesheetCollectionIterator<'a, S>
-where
- S: StylesheetInDocument + PartialEq + 'static,
-{
- type Item = &'a S;
-
- fn next(&mut self) -> Option<Self::Item> {
- self.0.next().map(|entry| &entry.sheet)
- }
-
- fn size_hint(&self) -> (usize, Option<usize>) {
- self.0.size_hint()
- }
-}
-
-/// An iterator over the flattened view of the stylesheet collections.
-#[derive(Clone)]
-pub struct StylesheetIterator<'a, S>
-where
- S: StylesheetInDocument + PartialEq + 'static,
-{
- origins: OriginSetIterator,
- collections: &'a PerOrigin<SheetCollection<S>>,
- current: Option<(Origin, StylesheetCollectionIterator<'a, S>)>,
-}
-
-impl<'a, S> Iterator for StylesheetIterator<'a, S>
-where
- S: StylesheetInDocument + PartialEq + 'static,
-{
- type Item = (&'a S, Origin);
-
- fn next(&mut self) -> Option<Self::Item> {
- loop {
- if self.current.is_none() {
- let next_origin = self.origins.next()?;
-
- self.current = Some((
- next_origin,
- self.collections.borrow_for_origin(&next_origin).iter(),
- ));
- }
-
- {
- let (origin, ref mut iter) = *self.current.as_mut().unwrap();
- if let Some(s) = iter.next() {
- return Some((s, origin));
- }
- }
-
- self.current = None;
- }
- }
-}
-
-/// The validity of the data in a given cascade origin.
-#[derive(Clone, Copy, Debug, Eq, MallocSizeOf, Ord, PartialEq, PartialOrd)]
-pub enum DataValidity {
- /// The origin is clean, all the data already there is valid, though we may
- /// have new sheets at the end.
- Valid = 0,
-
- /// The cascade data is invalid, but not the invalidation data (which is
- /// order-independent), and thus only the cascade data should be inserted.
- CascadeInvalid = 1,
-
- /// Everything needs to be rebuilt.
- FullyInvalid = 2,
-}
-
-impl Default for DataValidity {
- fn default() -> Self {
- DataValidity::Valid
- }
-}
-
-/// A struct to iterate over the different stylesheets to be flushed.
-pub struct DocumentStylesheetFlusher<'a, S>
-where
- S: StylesheetInDocument + PartialEq + 'static,
-{
- collections: &'a mut PerOrigin<SheetCollection<S>>,
- had_invalidations: bool,
-}
-
-/// The type of rebuild that we need to do for a given stylesheet.
-#[derive(Clone, Copy, Debug)]
-pub enum SheetRebuildKind {
- /// A full rebuild, of both cascade data and invalidation data.
- Full,
- /// A partial rebuild, of only the cascade data.
- CascadeOnly,
-}
-
-impl SheetRebuildKind {
- /// Whether the stylesheet invalidation data should be rebuilt.
- pub fn should_rebuild_invalidation(&self) -> bool {
- matches!(*self, SheetRebuildKind::Full)
- }
-}
-
-impl<'a, S> DocumentStylesheetFlusher<'a, S>
-where
- S: StylesheetInDocument + PartialEq + 'static,
-{
- /// Returns a flusher for `origin`.
- pub fn flush_origin(&mut self, origin: Origin) -> SheetCollectionFlusher<S> {
- self.collections.borrow_mut_for_origin(&origin).flush()
- }
-
- /// Returns the list of stylesheets for `origin`.
- ///
- /// Only used for UA sheets.
- pub fn origin_sheets(&mut self, origin: Origin) -> StylesheetCollectionIterator<S> {
- self.collections.borrow_mut_for_origin(&origin).iter()
- }
-
- /// Returns whether any DOM invalidations were processed as a result of the
- /// stylesheet flush.
- #[inline]
- pub fn had_invalidations(&self) -> bool {
- self.had_invalidations
- }
-}
-
-/// A flusher struct for a given collection, that takes care of returning the
-/// appropriate stylesheets that need work.
-pub struct SheetCollectionFlusher<'a, S>
-where
- S: StylesheetInDocument + PartialEq + 'static,
-{
- // TODO: This can be made an iterator again once
- // https://github.com/rust-lang/rust/pull/82771 lands on stable.
- entries: &'a mut [StylesheetSetEntry<S>],
- validity: DataValidity,
- dirty: bool,
-}
-
-impl<'a, S> SheetCollectionFlusher<'a, S>
-where
- S: StylesheetInDocument + PartialEq + 'static,
-{
- /// Whether the collection was originally dirty.
- #[inline]
- pub fn dirty(&self) -> bool {
- self.dirty
- }
-
- /// What the state of the sheet data is.
- #[inline]
- pub fn data_validity(&self) -> DataValidity {
- self.validity
- }
-
- /// Returns an iterator over the remaining list of sheets to consume.
- pub fn sheets<'b>(&'b self) -> impl Iterator<Item = &'b S> {
- self.entries.iter().map(|entry| &entry.sheet)
- }
-}
-
-impl<'a, S> SheetCollectionFlusher<'a, S>
-where
- S: StylesheetInDocument + PartialEq + 'static,
-{
- /// Iterates over all sheets and values that we have to invalidate.
- ///
- /// TODO(emilio): This would be nicer as an iterator but we can't do that
- /// until https://github.com/rust-lang/rust/pull/82771 stabilizes.
- ///
- /// Since we don't have a good use-case for partial iteration, this does the
- /// trick for now.
- pub fn each(self, mut callback: impl FnMut(&S, SheetRebuildKind) -> bool) {
- for potential_sheet in self.entries.iter_mut() {
- let committed = mem::replace(&mut potential_sheet.committed, true);
- let rebuild_kind = if !committed {
- // If the sheet was uncommitted, we need to do a full rebuild
- // anyway.
- SheetRebuildKind::Full
- } else {
- match self.validity {
- DataValidity::Valid => continue,
- DataValidity::CascadeInvalid => SheetRebuildKind::CascadeOnly,
- DataValidity::FullyInvalid => SheetRebuildKind::Full,
- }
- };
-
- if !callback(&potential_sheet.sheet, rebuild_kind) {
- return;
- }
- }
- }
-}
-
-#[derive(MallocSizeOf)]
-struct SheetCollection<S>
-where
- S: StylesheetInDocument + PartialEq + 'static,
-{
- /// The actual list of stylesheets.
- ///
- /// This is only a list of top-level stylesheets, and as such it doesn't
- /// include recursive `@import` rules.
- entries: Vec<StylesheetSetEntry<S>>,
-
- /// The validity of the data that was already there for a given origin.
- ///
- /// Note that an origin may appear on `origins_dirty`, but still have
- /// `DataValidity::Valid`, if only sheets have been appended into it (in
- /// which case the existing data is valid, but the origin needs to be
- /// rebuilt).
- data_validity: DataValidity,
-
- /// Whether anything in the collection has changed. Note that this is
- /// different from `data_validity`, in the sense that after a sheet append,
- /// the data validity is still `Valid`, but we need to be marked as dirty.
- dirty: bool,
-}
-
-impl<S> Default for SheetCollection<S>
-where
- S: StylesheetInDocument + PartialEq + 'static,
-{
- fn default() -> Self {
- Self {
- entries: vec![],
- data_validity: DataValidity::Valid,
- dirty: false,
- }
- }
-}
-
-impl<S> SheetCollection<S>
-where
- S: StylesheetInDocument + PartialEq + 'static,
-{
- /// Returns the number of stylesheets in the set.
- fn len(&self) -> usize {
- self.entries.len()
- }
-
- /// Returns the `index`th stylesheet in the set if present.
- fn get(&self, index: usize) -> Option<&S> {
- self.entries.get(index).map(|e| &e.sheet)
- }
-
- fn remove(&mut self, sheet: &S) {
- let index = self.entries.iter().position(|entry| entry.sheet == *sheet);
- if cfg!(feature = "gecko") && index.is_none() {
- // FIXME(emilio): Make Gecko's PresShell::AddUserSheet not suck.
- return;
- }
- let sheet = self.entries.remove(index.unwrap());
- // Removing sheets makes us tear down the whole cascade and invalidation
- // data, but only if the sheet has been involved in at least one flush.
- // Checking whether the sheet has been committed allows us to avoid
- // rebuilding the world when sites quickly append and remove a
- // stylesheet.
- //
- // See bug 1434756.
- if sheet.committed {
- self.set_data_validity_at_least(DataValidity::FullyInvalid);
- } else {
- self.dirty = true;
- }
- }
-
- fn contains(&self, sheet: &S) -> bool {
- self.entries.iter().any(|e| e.sheet == *sheet)
- }
-
- /// Appends a given sheet into the collection.
- fn append(&mut self, sheet: S) {
- debug_assert!(!self.contains(&sheet));
- self.entries.push(StylesheetSetEntry::new(sheet));
- // Appending sheets doesn't alter the validity of the existing data, so
- // we don't need to change `data_validity` here.
- //
- // But we need to be marked as dirty, otherwise we'll never add the new
- // sheet!
- self.dirty = true;
- }
-
- fn insert_before(&mut self, sheet: S, before_sheet: &S) {
- debug_assert!(!self.contains(&sheet));
-
- let index = self
- .entries
- .iter()
- .position(|entry| entry.sheet == *before_sheet)
- .expect("`before_sheet` stylesheet not found");
-
- // Inserting stylesheets somewhere but at the end changes the validity
- // of the cascade data, but not the invalidation data.
- self.set_data_validity_at_least(DataValidity::CascadeInvalid);
- self.entries.insert(index, StylesheetSetEntry::new(sheet));
- }
-
- fn set_data_validity_at_least(&mut self, validity: DataValidity) {
- use std::cmp;
-
- debug_assert_ne!(validity, DataValidity::Valid);
-
- self.dirty = true;
- self.data_validity = cmp::max(validity, self.data_validity);
- }
-
- /// Returns an iterator over the current list of stylesheets.
- fn iter(&self) -> StylesheetCollectionIterator<S> {
- StylesheetCollectionIterator(self.entries.iter())
- }
-
- fn flush(&mut self) -> SheetCollectionFlusher<S> {
- let dirty = mem::replace(&mut self.dirty, false);
- let validity = mem::replace(&mut self.data_validity, DataValidity::Valid);
-
- SheetCollectionFlusher {
- entries: &mut self.entries,
- dirty,
- validity,
- }
- }
-}
-
-/// The set of stylesheets effective for a given document.
-#[cfg_attr(feature = "servo", derive(MallocSizeOf))]
-pub struct DocumentStylesheetSet<S>
-where
- S: StylesheetInDocument + PartialEq + 'static,
-{
- /// The collections of sheets per each origin.
- collections: PerOrigin<SheetCollection<S>>,
-
- /// The invalidations for stylesheets added or removed from this document.
- invalidations: StylesheetInvalidationSet,
-}
-
-/// This macro defines methods common to DocumentStylesheetSet and
-/// AuthorStylesheetSet.
-///
-/// We could simplify the setup moving invalidations to SheetCollection, but
-/// that would imply not sharing invalidations across origins of the same
-/// documents, which is slightly annoying.
-macro_rules! sheet_set_methods {
- ($set_name:expr) => {
- fn collect_invalidations_for(
- &mut self,
- device: Option<&Device>,
- sheet: &S,
- guard: &SharedRwLockReadGuard,
- ) {
- if let Some(device) = device {
- self.invalidations
- .collect_invalidations_for(device, sheet, guard);
- }
- }
-
- /// Appends a new stylesheet to the current set.
- ///
- /// No device implies not computing invalidations.
- pub fn append_stylesheet(
- &mut self,
- device: Option<&Device>,
- sheet: S,
- guard: &SharedRwLockReadGuard,
- ) {
- debug!(concat!($set_name, "::append_stylesheet"));
- self.collect_invalidations_for(device, &sheet, guard);
- let collection = self.collection_for(&sheet);
- collection.append(sheet);
- }
-
- /// Insert a given stylesheet before another stylesheet in the document.
- pub fn insert_stylesheet_before(
- &mut self,
- device: Option<&Device>,
- sheet: S,
- before_sheet: S,
- guard: &SharedRwLockReadGuard,
- ) {
- debug!(concat!($set_name, "::insert_stylesheet_before"));
- self.collect_invalidations_for(device, &sheet, guard);
-
- let collection = self.collection_for(&sheet);
- collection.insert_before(sheet, &before_sheet);
- }
-
- /// Remove a given stylesheet from the set.
- pub fn remove_stylesheet(
- &mut self,
- device: Option<&Device>,
- sheet: S,
- guard: &SharedRwLockReadGuard,
- ) {
- debug!(concat!($set_name, "::remove_stylesheet"));
- self.collect_invalidations_for(device, &sheet, guard);
-
- let collection = self.collection_for(&sheet);
- collection.remove(&sheet)
- }
-
- /// Notify the set that a rule from a given stylesheet has changed
- /// somehow.
- pub fn rule_changed(
- &mut self,
- device: Option<&Device>,
- sheet: &S,
- rule: &CssRule,
- guard: &SharedRwLockReadGuard,
- change_kind: RuleChangeKind,
- ) {
- if let Some(device) = device {
- let quirks_mode = device.quirks_mode();
- self.invalidations.rule_changed(
- sheet,
- rule,
- guard,
- device,
- quirks_mode,
- change_kind,
- );
- }
-
- let validity = match change_kind {
- // Insertion / Removals need to rebuild both the cascade and
- // invalidation data. For generic changes this is conservative,
- // could be optimized on a per-case basis.
- RuleChangeKind::Generic | RuleChangeKind::Insertion | RuleChangeKind::Removal => {
- DataValidity::FullyInvalid
- },
- // TODO(emilio): This, in theory, doesn't need to invalidate
- // style data, if the rule we're modifying is actually in the
- // CascadeData already.
- //
- // But this is actually a bit tricky to prove, because when we
- // copy-on-write a stylesheet we don't bother doing a rebuild,
- // so we may still have rules from the original stylesheet
- // instead of the cloned one that we're modifying. So don't
- // bother for now and unconditionally rebuild, it's no worse
- // than what we were already doing anyway.
- //
- // Maybe we could record whether we saw a clone in this flush,
- // and if so do the conservative thing, otherwise just
- // early-return.
- RuleChangeKind::StyleRuleDeclarations => DataValidity::FullyInvalid,
- };
-
- let collection = self.collection_for(&sheet);
- collection.set_data_validity_at_least(validity);
- }
- };
-}
-
-impl<S> DocumentStylesheetSet<S>
-where
- S: StylesheetInDocument + PartialEq + 'static,
-{
- /// Create a new empty DocumentStylesheetSet.
- pub fn new() -> Self {
- Self {
- collections: Default::default(),
- invalidations: StylesheetInvalidationSet::new(),
- }
- }
-
- fn collection_for(&mut self, sheet: &S) -> &mut SheetCollection<S> {
- let origin = sheet.contents().origin;
- self.collections.borrow_mut_for_origin(&origin)
- }
-
- sheet_set_methods!("DocumentStylesheetSet");
-
- /// Returns the number of stylesheets in the set.
- pub fn len(&self) -> usize {
- self.collections
- .iter_origins()
- .fold(0, |s, (item, _)| s + item.len())
- }
-
- /// Returns the count of stylesheets for a given origin.
- #[inline]
- pub fn sheet_count(&self, origin: Origin) -> usize {
- self.collections.borrow_for_origin(&origin).len()
- }
-
- /// Returns the `index`th stylesheet in the set for the given origin.
- #[inline]
- pub fn get(&self, origin: Origin, index: usize) -> Option<&S> {
- self.collections.borrow_for_origin(&origin).get(index)
- }
-
- /// Returns whether the given set has changed from the last flush.
- pub fn has_changed(&self) -> bool {
- !self.invalidations.is_empty() ||
- self.collections
- .iter_origins()
- .any(|(collection, _)| collection.dirty)
- }
-
- /// Flush the current set, unmarking it as dirty, and returns a
- /// `DocumentStylesheetFlusher` in order to rebuild the stylist.
- pub fn flush<E>(
- &mut self,
- document_element: Option<E>,
- snapshots: Option<&SnapshotMap>,
- ) -> DocumentStylesheetFlusher<S>
- where
- E: TElement,
- {
- debug!("DocumentStylesheetSet::flush");
-
- let had_invalidations = self.invalidations.flush(document_element, snapshots);
-
- DocumentStylesheetFlusher {
- collections: &mut self.collections,
- had_invalidations,
- }
- }
-
- /// Flush stylesheets, but without running any of the invalidation passes.
- #[cfg(feature = "servo")]
- pub fn flush_without_invalidation(&mut self) -> OriginSet {
- debug!("DocumentStylesheetSet::flush_without_invalidation");
-
- let mut origins = OriginSet::empty();
- self.invalidations.clear();
-
- for (collection, origin) in self.collections.iter_mut_origins() {
- if collection.flush().dirty() {
- origins |= origin;
- }
- }
-
- origins
- }
-
- /// Return an iterator over the flattened view of all the stylesheets.
- pub fn iter(&self) -> StylesheetIterator<S> {
- StylesheetIterator {
- origins: OriginSet::all().iter(),
- collections: &self.collections,
- current: None,
- }
- }
-
- /// Mark the stylesheets for the specified origin as dirty, because
- /// something external may have invalidated it.
- pub fn force_dirty(&mut self, origins: OriginSet) {
- self.invalidations.invalidate_fully();
- for origin in origins.iter() {
- // We don't know what happened, assume the worse.
- self.collections
- .borrow_mut_for_origin(&origin)
- .set_data_validity_at_least(DataValidity::FullyInvalid);
- }
- }
-}
-
-/// The set of stylesheets effective for a given Shadow Root.
-#[derive(MallocSizeOf)]
-pub struct AuthorStylesheetSet<S>
-where
- S: StylesheetInDocument + PartialEq + 'static,
-{
- /// The actual style sheets.
- collection: SheetCollection<S>,
- /// The set of invalidations scheduled for this collection.
- invalidations: StylesheetInvalidationSet,
-}
-
-/// A struct to flush an author style sheet collection.
-pub struct AuthorStylesheetFlusher<'a, S>
-where
- S: StylesheetInDocument + PartialEq + 'static,
-{
- /// The actual flusher for the collection.
- pub sheets: SheetCollectionFlusher<'a, S>,
- /// Whether any sheet invalidation matched.
- pub had_invalidations: bool,
-}
-
-impl<S> AuthorStylesheetSet<S>
-where
- S: StylesheetInDocument + PartialEq + 'static,
-{
- /// Create a new empty AuthorStylesheetSet.
- #[inline]
- pub fn new() -> Self {
- Self {
- collection: Default::default(),
- invalidations: StylesheetInvalidationSet::new(),
- }
- }
-
- /// Whether anything has changed since the last time this was flushed.
- pub fn dirty(&self) -> bool {
- self.collection.dirty
- }
-
- /// Whether the collection is empty.
- pub fn is_empty(&self) -> bool {
- self.collection.len() == 0
- }
-
- /// Returns the `index`th stylesheet in the collection of author styles if present.
- pub fn get(&self, index: usize) -> Option<&S> {
- self.collection.get(index)
- }
-
- /// Returns the number of author stylesheets.
- pub fn len(&self) -> usize {
- self.collection.len()
- }
-
- fn collection_for(&mut self, _sheet: &S) -> &mut SheetCollection<S> {
- &mut self.collection
- }
-
- sheet_set_methods!("AuthorStylesheetSet");
-
- /// Iterate over the list of stylesheets.
- pub fn iter(&self) -> StylesheetCollectionIterator<S> {
- self.collection.iter()
- }
-
- /// Mark the sheet set dirty, as appropriate.
- pub fn force_dirty(&mut self) {
- self.invalidations.invalidate_fully();
- self.collection
- .set_data_validity_at_least(DataValidity::FullyInvalid);
- }
-
- /// Flush the stylesheets for this author set.
- ///
- /// `host` is the root of the affected subtree, like the shadow host, for
- /// example.
- pub fn flush<E>(
- &mut self,
- host: Option<E>,
- snapshots: Option<&SnapshotMap>,
- ) -> AuthorStylesheetFlusher<S>
- where
- E: TElement,
- {
- let had_invalidations = self.invalidations.flush(host, snapshots);
- AuthorStylesheetFlusher {
- sheets: self.collection.flush(),
- had_invalidations,
- }
- }
-}
diff --git a/components/style/stylesheets/container_rule.rs b/components/style/stylesheets/container_rule.rs
deleted file mode 100644
index f9d488b9b49..00000000000
--- a/components/style/stylesheets/container_rule.rs
+++ /dev/null
@@ -1,632 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
-
-//! A [`@container`][container] rule.
-//!
-//! [container]: https://drafts.csswg.org/css-contain-3/#container-rule
-
-use crate::computed_value_flags::ComputedValueFlags;
-use crate::dom::TElement;
-use crate::logical_geometry::{LogicalSize, WritingMode};
-use crate::media_queries::Device;
-use crate::parser::ParserContext;
-use crate::properties::ComputedValues;
-use crate::queries::condition::KleeneValue;
-use crate::queries::feature::{AllowsRanges, Evaluator, FeatureFlags, QueryFeatureDescription};
-use crate::queries::values::Orientation;
-use crate::queries::{FeatureType, QueryCondition};
-use crate::shared_lock::{
- DeepCloneParams, DeepCloneWithLock, Locked, SharedRwLock, SharedRwLockReadGuard, ToCssWithGuard,
-};
-use crate::str::CssStringWriter;
-use crate::stylesheets::CssRules;
-use crate::values::computed::{CSSPixelLength, ContainerType, Context, Ratio};
-use crate::values::specified::ContainerName;
-use app_units::Au;
-use cssparser::{Parser, SourceLocation};
-use euclid::default::Size2D;
-#[cfg(feature = "gecko")]
-use malloc_size_of::{MallocSizeOfOps, MallocUnconditionalShallowSizeOf};
-use servo_arc::Arc;
-use std::fmt::{self, Write};
-use style_traits::{CssWriter, ParseError, ToCss};
-
-/// A container rule.
-#[derive(Debug, ToShmem)]
-pub struct ContainerRule {
- /// The container query and name.
- pub condition: Arc<ContainerCondition>,
- /// The nested rules inside the block.
- pub rules: Arc<Locked<CssRules>>,
- /// The source position where this rule was found.
- pub source_location: SourceLocation,
-}
-
-impl ContainerRule {
- /// Returns the query condition.
- pub fn query_condition(&self) -> &QueryCondition {
- &self.condition.condition
- }
-
- /// Returns the query name filter.
- pub fn container_name(&self) -> &ContainerName {
- &self.condition.name
- }
-
- /// Measure heap usage.
- #[cfg(feature = "gecko")]
- pub fn size_of(&self, guard: &SharedRwLockReadGuard, ops: &mut MallocSizeOfOps) -> usize {
- // Measurement of other fields may be added later.
- self.rules.unconditional_shallow_size_of(ops) +
- self.rules.read_with(guard).size_of(guard, ops)
- }
-}
-
-impl DeepCloneWithLock for ContainerRule {
- fn deep_clone_with_lock(
- &self,
- lock: &SharedRwLock,
- guard: &SharedRwLockReadGuard,
- params: &DeepCloneParams,
- ) -> Self {
- let rules = self.rules.read_with(guard);
- Self {
- condition: self.condition.clone(),
- rules: Arc::new(lock.wrap(rules.deep_clone_with_lock(lock, guard, params))),
- source_location: self.source_location.clone(),
- }
- }
-}
-
-impl ToCssWithGuard for ContainerRule {
- fn to_css(&self, guard: &SharedRwLockReadGuard, dest: &mut CssStringWriter) -> fmt::Result {
- dest.write_str("@container ")?;
- {
- let mut writer = CssWriter::new(dest);
- if !self.condition.name.is_none() {
- self.condition.name.to_css(&mut writer)?;
- writer.write_char(' ')?;
- }
- self.condition.condition.to_css(&mut writer)?;
- }
- self.rules.read_with(guard).to_css_block(guard, dest)
- }
-}
-
-/// A container condition and filter, combined.
-#[derive(Debug, ToShmem, ToCss)]
-pub struct ContainerCondition {
- #[css(skip_if = "ContainerName::is_none")]
- name: ContainerName,
- condition: QueryCondition,
- #[css(skip)]
- flags: FeatureFlags,
-}
-
-/// The result of a successful container query lookup.
-pub struct ContainerLookupResult<E> {
- /// The relevant container.
- pub element: E,
- /// The sizing / writing-mode information of the container.
- pub info: ContainerInfo,
- /// The style of the element.
- pub style: Arc<ComputedValues>,
-}
-
-fn container_type_axes(ty_: ContainerType, wm: WritingMode) -> FeatureFlags {
- match ty_ {
- ContainerType::Size => FeatureFlags::all_container_axes(),
- ContainerType::InlineSize => {
- let physical_axis = if wm.is_vertical() {
- FeatureFlags::CONTAINER_REQUIRES_HEIGHT_AXIS
- } else {
- FeatureFlags::CONTAINER_REQUIRES_WIDTH_AXIS
- };
- FeatureFlags::CONTAINER_REQUIRES_INLINE_AXIS | physical_axis
- },
- ContainerType::Normal => FeatureFlags::empty(),
- }
-}
-
-enum TraversalResult<T> {
- InProgress,
- StopTraversal,
- Done(T),
-}
-
-fn traverse_container<E, F, R>(
- mut e: E,
- originating_element_style: Option<&ComputedValues>,
- evaluator: F,
-) -> Option<(E, R)>
-where
- E: TElement,
- F: Fn(E, Option<&ComputedValues>) -> TraversalResult<R>,
-{
- if originating_element_style.is_some() {
- match evaluator(e, originating_element_style) {
- TraversalResult::InProgress => {},
- TraversalResult::StopTraversal => return None,
- TraversalResult::Done(result) => return Some((e, result)),
- }
- }
- while let Some(element) = e.traversal_parent() {
- match evaluator(element, None) {
- TraversalResult::InProgress => {},
- TraversalResult::StopTraversal => return None,
- TraversalResult::Done(result) => return Some((element, result)),
- }
- e = element;
- }
-
- None
-}
-
-impl ContainerCondition {
- /// Parse a container condition.
- pub fn parse<'a>(
- context: &ParserContext,
- input: &mut Parser<'a, '_>,
- ) -> Result<Self, ParseError<'a>> {
- let name = input
- .try_parse(|input| ContainerName::parse_for_query(context, input))
- .ok()
- .unwrap_or_else(ContainerName::none);
- let condition = QueryCondition::parse(context, input, FeatureType::Container)?;
- let flags = condition.cumulative_flags();
- Ok(Self {
- name,
- condition,
- flags,
- })
- }
-
- fn valid_container_info<E>(
- &self,
- potential_container: E,
- originating_element_style: Option<&ComputedValues>,
- ) -> TraversalResult<ContainerLookupResult<E>>
- where
- E: TElement,
- {
- let data;
- let style = match originating_element_style {
- Some(s) => s,
- None => {
- data = match potential_container.borrow_data() {
- Some(d) => d,
- None => return TraversalResult::InProgress,
- };
- &**data.styles.primary()
- },
- };
- let wm = style.writing_mode;
- let box_style = style.get_box();
-
- // Filter by container-type.
- let container_type = box_style.clone_container_type();
- let available_axes = container_type_axes(container_type, wm);
- if !available_axes.contains(self.flags.container_axes()) {
- return TraversalResult::InProgress;
- }
-
- // Filter by container-name.
- let container_name = box_style.clone_container_name();
- for filter_name in self.name.0.iter() {
- if !container_name.0.contains(filter_name) {
- return TraversalResult::InProgress;
- }
- }
-
- let size = potential_container.query_container_size(&box_style.clone_display());
- let style = style.to_arc();
- TraversalResult::Done(ContainerLookupResult {
- element: potential_container,
- info: ContainerInfo { size, wm },
- style,
- })
- }
-
- /// Performs container lookup for a given element.
- pub fn find_container<E>(
- &self,
- e: E,
- originating_element_style: Option<&ComputedValues>,
- ) -> Option<ContainerLookupResult<E>>
- where
- E: TElement,
- {
- match traverse_container(
- e,
- originating_element_style,
- |element, originating_element_style| {
- self.valid_container_info(element, originating_element_style)
- },
- ) {
- Some((_, result)) => Some(result),
- None => None,
- }
- }
-
- /// Tries to match a container query condition for a given element.
- pub(crate) fn matches<E>(
- &self,
- device: &Device,
- element: E,
- originating_element_style: Option<&ComputedValues>,
- invalidation_flags: &mut ComputedValueFlags,
- ) -> KleeneValue
- where
- E: TElement,
- {
- let result = self.find_container(element, originating_element_style);
- let (container, info) = match result {
- Some(r) => (Some(r.element), Some((r.info, r.style))),
- None => (None, None),
- };
- // Set up the lookup for the container in question, as the condition may be using container query lengths.
- let size_query_container_lookup = ContainerSizeQuery::for_option_element(container, None);
- Context::for_container_query_evaluation(
- device,
- info,
- size_query_container_lookup,
- |context| {
- let matches = self.condition.matches(context);
- if context
- .style()
- .flags()
- .contains(ComputedValueFlags::USES_VIEWPORT_UNITS)
- {
- // TODO(emilio): Might need something similar to improve
- // invalidation of font relative container-query lengths.
- invalidation_flags
- .insert(ComputedValueFlags::USES_VIEWPORT_UNITS_ON_CONTAINER_QUERIES);
- }
- matches
- },
- )
- }
-}
-
-/// Information needed to evaluate an individual container query.
-#[derive(Copy, Clone)]
-pub struct ContainerInfo {
- size: Size2D<Option<Au>>,
- wm: WritingMode,
-}
-
-impl ContainerInfo {
- fn size(&self) -> Option<Size2D<Au>> {
- Some(Size2D::new(self.size.width?, self.size.height?))
- }
-}
-
-fn eval_width(context: &Context) -> Option<CSSPixelLength> {
- let info = context.container_info.as_ref()?;
- Some(CSSPixelLength::new(info.size.width?.to_f32_px()))
-}
-
-fn eval_height(context: &Context) -> Option<CSSPixelLength> {
- let info = context.container_info.as_ref()?;
- Some(CSSPixelLength::new(info.size.height?.to_f32_px()))
-}
-
-fn eval_inline_size(context: &Context) -> Option<CSSPixelLength> {
- let info = context.container_info.as_ref()?;
- Some(CSSPixelLength::new(
- LogicalSize::from_physical(info.wm, info.size)
- .inline?
- .to_f32_px(),
- ))
-}
-
-fn eval_block_size(context: &Context) -> Option<CSSPixelLength> {
- let info = context.container_info.as_ref()?;
- Some(CSSPixelLength::new(
- LogicalSize::from_physical(info.wm, info.size)
- .block?
- .to_f32_px(),
- ))
-}
-
-fn eval_aspect_ratio(context: &Context) -> Option<Ratio> {
- let info = context.container_info.as_ref()?;
- Some(Ratio::new(
- info.size.width?.0 as f32,
- info.size.height?.0 as f32,
- ))
-}
-
-fn eval_orientation(context: &Context, value: Option<Orientation>) -> KleeneValue {
- let size = match context.container_info.as_ref().and_then(|info| info.size()) {
- Some(size) => size,
- None => return KleeneValue::Unknown,
- };
- KleeneValue::from(Orientation::eval(size, value))
-}
-
-/// https://drafts.csswg.org/css-contain-3/#container-features
-///
-/// TODO: Support style queries, perhaps.
-pub static CONTAINER_FEATURES: [QueryFeatureDescription; 6] = [
- feature!(
- atom!("width"),
- AllowsRanges::Yes,
- Evaluator::OptionalLength(eval_width),
- FeatureFlags::CONTAINER_REQUIRES_WIDTH_AXIS,
- ),
- feature!(
- atom!("height"),
- AllowsRanges::Yes,
- Evaluator::OptionalLength(eval_height),
- FeatureFlags::CONTAINER_REQUIRES_HEIGHT_AXIS,
- ),
- feature!(
- atom!("inline-size"),
- AllowsRanges::Yes,
- Evaluator::OptionalLength(eval_inline_size),
- FeatureFlags::CONTAINER_REQUIRES_INLINE_AXIS,
- ),
- feature!(
- atom!("block-size"),
- AllowsRanges::Yes,
- Evaluator::OptionalLength(eval_block_size),
- FeatureFlags::CONTAINER_REQUIRES_BLOCK_AXIS,
- ),
- feature!(
- atom!("aspect-ratio"),
- AllowsRanges::Yes,
- Evaluator::OptionalNumberRatio(eval_aspect_ratio),
- // XXX from_bits_truncate is const, but the pipe operator isn't, so this
- // works around it.
- FeatureFlags::from_bits_truncate(
- FeatureFlags::CONTAINER_REQUIRES_BLOCK_AXIS.bits() |
- FeatureFlags::CONTAINER_REQUIRES_INLINE_AXIS.bits()
- ),
- ),
- feature!(
- atom!("orientation"),
- AllowsRanges::No,
- keyword_evaluator!(eval_orientation, Orientation),
- FeatureFlags::from_bits_truncate(
- FeatureFlags::CONTAINER_REQUIRES_BLOCK_AXIS.bits() |
- FeatureFlags::CONTAINER_REQUIRES_INLINE_AXIS.bits()
- ),
- ),
-];
-
-/// Result of a container size query, signifying the hypothetical containment boundary in terms of physical axes.
-/// Defined by up to two size containers. Queries on logical axes are resolved with respect to the querying
-/// element's writing mode.
-#[derive(Copy, Clone, Default)]
-pub struct ContainerSizeQueryResult {
- width: Option<Au>,
- height: Option<Au>,
-}
-
-impl ContainerSizeQueryResult {
- fn get_viewport_size(context: &Context) -> Size2D<Au> {
- use crate::values::specified::ViewportVariant;
- context.viewport_size_for_viewport_unit_resolution(ViewportVariant::Small)
- }
-
- fn get_logical_viewport_size(context: &Context) -> LogicalSize<Au> {
- LogicalSize::from_physical(
- context.builder.writing_mode,
- Self::get_viewport_size(context),
- )
- }
-
- /// Get the inline-size of the query container.
- pub fn get_container_inline_size(&self, context: &Context) -> Au {
- if context.builder.writing_mode.is_horizontal() {
- if let Some(w) = self.width {
- return w;
- }
- } else {
- if let Some(h) = self.height {
- return h;
- }
- }
- Self::get_logical_viewport_size(context).inline
- }
-
- /// Get the block-size of the query container.
- pub fn get_container_block_size(&self, context: &Context) -> Au {
- if context.builder.writing_mode.is_horizontal() {
- self.get_container_height(context)
- } else {
- self.get_container_width(context)
- }
- }
-
- /// Get the width of the query container.
- pub fn get_container_width(&self, context: &Context) -> Au {
- if let Some(w) = self.width {
- return w;
- }
- Self::get_viewport_size(context).width
- }
-
- /// Get the height of the query container.
- pub fn get_container_height(&self, context: &Context) -> Au {
- if let Some(h) = self.height {
- return h;
- }
- Self::get_viewport_size(context).height
- }
-
- // Merge the result of a subsequent lookup, preferring the initial result.
- fn merge(self, new_result: Self) -> Self {
- let mut result = self;
- if let Some(width) = new_result.width {
- result.width.get_or_insert(width);
- }
- if let Some(height) = new_result.height {
- result.height.get_or_insert(height);
- }
- result
- }
-
- fn is_complete(&self) -> bool {
- self.width.is_some() && self.height.is_some()
- }
-}
-
-/// Unevaluated lazy container size query.
-pub enum ContainerSizeQuery<'a> {
- /// Query prior to evaluation.
- NotEvaluated(Box<dyn Fn() -> ContainerSizeQueryResult + 'a>),
- /// Cached evaluated result.
- Evaluated(ContainerSizeQueryResult),
-}
-
-impl<'a> ContainerSizeQuery<'a> {
- fn evaluate_potential_size_container<E>(
- e: E,
- originating_element_style: Option<&ComputedValues>,
- ) -> TraversalResult<ContainerSizeQueryResult>
- where
- E: TElement,
- {
- let data;
- let style = match originating_element_style {
- Some(s) => s,
- None => {
- data = match e.borrow_data() {
- Some(d) => d,
- None => return TraversalResult::InProgress,
- };
- &**data.styles.primary()
- },
- };
- if !style
- .flags
- .contains(ComputedValueFlags::SELF_OR_ANCESTOR_HAS_SIZE_CONTAINER_TYPE)
- {
- // We know we won't find a size container.
- return TraversalResult::StopTraversal;
- }
-
- let wm = style.writing_mode;
- let box_style = style.get_box();
-
- let container_type = box_style.clone_container_type();
- let size = e.query_container_size(&box_style.clone_display());
- match container_type {
- ContainerType::Size => TraversalResult::Done(ContainerSizeQueryResult {
- width: size.width,
- height: size.height,
- }),
- ContainerType::InlineSize => {
- if wm.is_horizontal() {
- TraversalResult::Done(ContainerSizeQueryResult {
- width: size.width,
- height: None,
- })
- } else {
- TraversalResult::Done(ContainerSizeQueryResult {
- width: None,
- height: size.height,
- })
- }
- },
- ContainerType::Normal => TraversalResult::InProgress,
- }
- }
-
- /// Find the query container size for a given element. Meant to be used as a callback for new().
- fn lookup<E>(
- element: E,
- originating_element_style: Option<&ComputedValues>,
- ) -> ContainerSizeQueryResult
- where
- E: TElement + 'a,
- {
- match traverse_container(
- element,
- originating_element_style,
- |e, originating_element_style| {
- Self::evaluate_potential_size_container(e, originating_element_style)
- },
- ) {
- Some((container, result)) => {
- if result.is_complete() {
- result
- } else {
- // Traverse up from the found size container to see if we can get a complete containment.
- result.merge(Self::lookup(container, None))
- }
- },
- None => ContainerSizeQueryResult::default(),
- }
- }
-
- /// Create a new instance of the container size query for given element, with a deferred lookup callback.
- pub fn for_element<E>(element: E, originating_element_style: Option<&'a ComputedValues>) -> Self
- where
- E: TElement + 'a,
- {
- let parent;
- let data;
- let style = match originating_element_style {
- Some(s) => Some(s),
- None => {
- // No need to bother if we're the top element.
- parent = match element.traversal_parent() {
- Some(parent) => parent,
- None => return Self::none(),
- };
- data = parent.borrow_data();
- data.as_ref().map(|data| &**data.styles.primary())
- },
- };
- let should_traverse = match style {
- Some(style) => style
- .flags
- .contains(ComputedValueFlags::SELF_OR_ANCESTOR_HAS_SIZE_CONTAINER_TYPE),
- None => true, // `display: none`, still want to show a correct computed value, so give it a try.
- };
- if should_traverse {
- return Self::NotEvaluated(Box::new(move || {
- Self::lookup(element, originating_element_style)
- }));
- }
- Self::none()
- }
-
- /// Create a new instance, but with optional element.
- pub fn for_option_element<E>(
- element: Option<E>,
- originating_element_style: Option<&'a ComputedValues>,
- ) -> Self
- where
- E: TElement + 'a,
- {
- if let Some(e) = element {
- Self::for_element(e, originating_element_style)
- } else {
- Self::none()
- }
- }
-
- /// Create a query that evaluates to empty, for cases where container size query is not required.
- pub fn none() -> Self {
- ContainerSizeQuery::Evaluated(ContainerSizeQueryResult::default())
- }
-
- /// Get the result of the container size query, doing the lookup if called for the first time.
- pub fn get(&mut self) -> ContainerSizeQueryResult {
- match self {
- Self::NotEvaluated(lookup) => {
- *self = Self::Evaluated((lookup)());
- match self {
- Self::Evaluated(info) => *info,
- _ => unreachable!("Just evaluated but not set?"),
- }
- },
- Self::Evaluated(info) => *info,
- }
- }
-}
diff --git a/components/style/stylesheets/counter_style_rule.rs b/components/style/stylesheets/counter_style_rule.rs
deleted file mode 100644
index 974b76b8060..00000000000
--- a/components/style/stylesheets/counter_style_rule.rs
+++ /dev/null
@@ -1,7 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
-
-#![allow(missing_docs)]
-
-pub use crate::counter_style::CounterStyleRuleData as CounterStyleRule;
diff --git a/components/style/stylesheets/document_rule.rs b/components/style/stylesheets/document_rule.rs
deleted file mode 100644
index 75edab308db..00000000000
--- a/components/style/stylesheets/document_rule.rs
+++ /dev/null
@@ -1,305 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
-
-//! [@document rules](https://www.w3.org/TR/2012/WD-css3-conditional-20120911/#at-document)
-//! initially in CSS Conditional Rules Module Level 3, @document has been postponed to the level 4.
-//! We implement the prefixed `@-moz-document`.
-
-use crate::media_queries::Device;
-use crate::parser::{Parse, ParserContext};
-use crate::shared_lock::{DeepCloneParams, DeepCloneWithLock, Locked};
-use crate::shared_lock::{SharedRwLock, SharedRwLockReadGuard, ToCssWithGuard};
-use crate::str::CssStringWriter;
-use crate::stylesheets::CssRules;
-use crate::values::CssUrl;
-use cssparser::{BasicParseErrorKind, Parser, SourceLocation};
-#[cfg(feature = "gecko")]
-use malloc_size_of::{MallocSizeOfOps, MallocUnconditionalShallowSizeOf};
-use servo_arc::Arc;
-use std::fmt::{self, Write};
-use style_traits::{CssWriter, ParseError, StyleParseErrorKind, ToCss};
-
-#[derive(Debug, ToShmem)]
-/// A @-moz-document rule
-pub struct DocumentRule {
- /// The parsed condition
- pub condition: DocumentCondition,
- /// Child rules
- pub rules: Arc<Locked<CssRules>>,
- /// The line and column of the rule's source code.
- pub source_location: SourceLocation,
-}
-
-impl DocumentRule {
- /// Measure heap usage.
- #[cfg(feature = "gecko")]
- pub fn size_of(&self, guard: &SharedRwLockReadGuard, ops: &mut MallocSizeOfOps) -> usize {
- // Measurement of other fields may be added later.
- self.rules.unconditional_shallow_size_of(ops) +
- self.rules.read_with(guard).size_of(guard, ops)
- }
-}
-
-impl ToCssWithGuard for DocumentRule {
- fn to_css(&self, guard: &SharedRwLockReadGuard, dest: &mut CssStringWriter) -> fmt::Result {
- dest.write_str("@-moz-document ")?;
- self.condition.to_css(&mut CssWriter::new(dest))?;
- dest.write_str(" {")?;
- for rule in self.rules.read_with(guard).0.iter() {
- dest.write_char(' ')?;
- rule.to_css(guard, dest)?;
- }
- dest.write_str(" }")
- }
-}
-
-impl DeepCloneWithLock for DocumentRule {
- /// Deep clones this DocumentRule.
- fn deep_clone_with_lock(
- &self,
- lock: &SharedRwLock,
- guard: &SharedRwLockReadGuard,
- params: &DeepCloneParams,
- ) -> Self {
- let rules = self.rules.read_with(guard);
- DocumentRule {
- condition: self.condition.clone(),
- rules: Arc::new(lock.wrap(rules.deep_clone_with_lock(lock, guard, params))),
- source_location: self.source_location.clone(),
- }
- }
-}
-
-/// The kind of media document that the rule will match.
-#[derive(Clone, Copy, Debug, Parse, PartialEq, ToCss, ToShmem)]
-#[allow(missing_docs)]
-pub enum MediaDocumentKind {
- All,
- Plugin,
- Image,
- Video,
-}
-
-/// A matching function for a `@document` rule's condition.
-#[derive(Clone, Debug, ToCss, ToShmem)]
-pub enum DocumentMatchingFunction {
- /// Exact URL matching function. It evaluates to true whenever the
- /// URL of the document being styled is exactly the URL given.
- Url(CssUrl),
- /// URL prefix matching function. It evaluates to true whenever the
- /// URL of the document being styled has the argument to the
- /// function as an initial substring (which is true when the two
- /// strings are equal). When the argument is the empty string,
- /// it evaluates to true for all documents.
- #[css(function)]
- UrlPrefix(String),
- /// Domain matching function. It evaluates to true whenever the URL
- /// of the document being styled has a host subcomponent and that
- /// host subcomponent is exactly the argument to the ‘domain()’
- /// function or a final substring of the host component is a
- /// period (U+002E) immediately followed by the argument to the
- /// ‘domain()’ function.
- #[css(function)]
- Domain(String),
- /// Regular expression matching function. It evaluates to true
- /// whenever the regular expression matches the entirety of the URL
- /// of the document being styled.
- #[css(function)]
- Regexp(String),
- /// Matching function for a media document.
- #[css(function)]
- MediaDocument(MediaDocumentKind),
- /// Matching function for a plain-text document.
- #[css(function)]
- PlainTextDocument(()),
- /// Matching function for a document that can be observed by other content
- /// documents.
- #[css(function)]
- UnobservableDocument(()),
-}
-
-macro_rules! parse_quoted_or_unquoted_string {
- ($input:ident, $url_matching_function:expr) => {
- $input.parse_nested_block(|input| {
- let start = input.position();
- input
- .parse_entirely(|input| {
- let string = input.expect_string()?;
- Ok($url_matching_function(string.as_ref().to_owned()))
- })
- .or_else(|_: ParseError| {
- while let Ok(_) = input.next() {}
- Ok($url_matching_function(input.slice_from(start).to_string()))
- })
- })
- };
-}
-
-impl DocumentMatchingFunction {
- /// Parse a URL matching function for a`@document` rule's condition.
- pub fn parse<'i, 't>(
- context: &ParserContext,
- input: &mut Parser<'i, 't>,
- ) -> Result<Self, ParseError<'i>> {
- if let Ok(url) = input.try_parse(|input| CssUrl::parse(context, input)) {
- return Ok(DocumentMatchingFunction::Url(url));
- }
-
- let location = input.current_source_location();
- let function = input.expect_function()?.clone();
- match_ignore_ascii_case! { &function,
- "url-prefix" => {
- parse_quoted_or_unquoted_string!(input, DocumentMatchingFunction::UrlPrefix)
- },
- "domain" => {
- parse_quoted_or_unquoted_string!(input, DocumentMatchingFunction::Domain)
- },
- "regexp" => {
- input.parse_nested_block(|input| {
- Ok(DocumentMatchingFunction::Regexp(
- input.expect_string()?.as_ref().to_owned(),
- ))
- })
- },
- "media-document" => {
- input.parse_nested_block(|input| {
- let kind = MediaDocumentKind::parse(input)?;
- Ok(DocumentMatchingFunction::MediaDocument(kind))
- })
- },
-
- "plain-text-document" => {
- input.parse_nested_block(|input| {
- input.expect_exhausted()?;
- Ok(DocumentMatchingFunction::PlainTextDocument(()))
- })
- },
-
- "unobservable-document" => {
- input.parse_nested_block(|input| {
- input.expect_exhausted()?;
- Ok(DocumentMatchingFunction::UnobservableDocument(()))
- })
- },
-
- _ => {
- Err(location.new_custom_error(
- StyleParseErrorKind::UnexpectedFunction(function.clone())
- ))
- },
- }
- }
-
- #[cfg(feature = "gecko")]
- /// Evaluate a URL matching function.
- pub fn evaluate(&self, device: &Device) -> bool {
- use crate::gecko_bindings::bindings::Gecko_DocumentRule_UseForPresentation;
- use crate::gecko_bindings::structs::DocumentMatchingFunction as GeckoDocumentMatchingFunction;
- use nsstring::nsCStr;
-
- let func = match *self {
- DocumentMatchingFunction::Url(_) => GeckoDocumentMatchingFunction::URL,
- DocumentMatchingFunction::UrlPrefix(_) => GeckoDocumentMatchingFunction::URLPrefix,
- DocumentMatchingFunction::Domain(_) => GeckoDocumentMatchingFunction::Domain,
- DocumentMatchingFunction::Regexp(_) => GeckoDocumentMatchingFunction::RegExp,
- DocumentMatchingFunction::MediaDocument(_) => {
- GeckoDocumentMatchingFunction::MediaDocument
- },
- DocumentMatchingFunction::PlainTextDocument(..) => {
- GeckoDocumentMatchingFunction::PlainTextDocument
- },
- DocumentMatchingFunction::UnobservableDocument(..) => {
- GeckoDocumentMatchingFunction::UnobservableDocument
- },
- };
-
- let pattern = nsCStr::from(match *self {
- DocumentMatchingFunction::Url(ref url) => url.as_str(),
- DocumentMatchingFunction::UrlPrefix(ref pat) |
- DocumentMatchingFunction::Domain(ref pat) |
- DocumentMatchingFunction::Regexp(ref pat) => pat,
- DocumentMatchingFunction::MediaDocument(kind) => match kind {
- MediaDocumentKind::All => "all",
- MediaDocumentKind::Image => "image",
- MediaDocumentKind::Plugin => "plugin",
- MediaDocumentKind::Video => "video",
- },
- DocumentMatchingFunction::PlainTextDocument(()) |
- DocumentMatchingFunction::UnobservableDocument(()) => "",
- });
- unsafe { Gecko_DocumentRule_UseForPresentation(device.document(), &*pattern, func) }
- }
-
- #[cfg(not(feature = "gecko"))]
- /// Evaluate a URL matching function.
- pub fn evaluate(&self, _: &Device) -> bool {
- false
- }
-}
-
-/// A `@document` rule's condition.
-///
-/// <https://www.w3.org/TR/2012/WD-css3-conditional-20120911/#at-document>
-///
-/// The `@document` rule's condition is written as a comma-separated list of
-/// URL matching functions, and the condition evaluates to true whenever any
-/// one of those functions evaluates to true.
-#[derive(Clone, Debug, ToCss, ToShmem)]
-#[css(comma)]
-pub struct DocumentCondition(#[css(iterable)] Vec<DocumentMatchingFunction>);
-
-impl DocumentCondition {
- /// Parse a document condition.
- pub fn parse<'i, 't>(
- context: &ParserContext,
- input: &mut Parser<'i, 't>,
- ) -> Result<Self, ParseError<'i>> {
- let conditions =
- input.parse_comma_separated(|input| DocumentMatchingFunction::parse(context, input))?;
-
- let condition = DocumentCondition(conditions);
- if !condition.allowed_in(context) {
- return Err(input.new_error(BasicParseErrorKind::AtRuleInvalid("-moz-document".into())));
- }
- Ok(condition)
- }
-
- /// Evaluate a document condition.
- pub fn evaluate(&self, device: &Device) -> bool {
- self.0
- .iter()
- .any(|url_matching_function| url_matching_function.evaluate(device))
- }
-
- #[cfg(feature = "servo")]
- fn allowed_in(&self, _: &ParserContext) -> bool {
- false
- }
-
- #[cfg(feature = "gecko")]
- fn allowed_in(&self, context: &ParserContext) -> bool {
- use static_prefs::pref;
-
- if context.in_ua_or_chrome_sheet() {
- return true;
- }
-
- if pref!("layout.css.moz-document.content.enabled") {
- return true;
- }
-
- // Allow a single url-prefix() for compatibility.
- //
- // See bug 1446470 and dependencies.
- if self.0.len() != 1 {
- return false;
- }
-
- // NOTE(emilio): This technically allows url-prefix("") too, but...
- match self.0[0] {
- DocumentMatchingFunction::UrlPrefix(ref prefix) => prefix.is_empty(),
- _ => false,
- }
- }
-}
diff --git a/components/style/stylesheets/font_feature_values_rule.rs b/components/style/stylesheets/font_feature_values_rule.rs
deleted file mode 100644
index 06016ec2bd9..00000000000
--- a/components/style/stylesheets/font_feature_values_rule.rs
+++ /dev/null
@@ -1,490 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
-
-//! The [`@font-feature-values`][font-feature-values] at-rule.
-//!
-//! [font-feature-values]: https://drafts.csswg.org/css-fonts-3/#at-font-feature-values-rule
-
-use crate::error_reporting::ContextualParseError;
-#[cfg(feature = "gecko")]
-use crate::gecko_bindings::bindings::Gecko_AppendFeatureValueHashEntry;
-#[cfg(feature = "gecko")]
-use crate::gecko_bindings::structs::{self, gfxFontFeatureValueSet, nsTArray};
-use crate::parser::{Parse, ParserContext};
-use crate::shared_lock::{SharedRwLockReadGuard, ToCssWithGuard};
-use crate::str::CssStringWriter;
-use crate::stylesheets::CssRuleType;
-use crate::values::computed::font::FamilyName;
-use crate::values::serialize_atom_identifier;
-use crate::Atom;
-use cssparser::{
- AtRuleParser, BasicParseErrorKind, CowRcStr, DeclarationParser, Parser, ParserState,
- QualifiedRuleParser, RuleBodyItemParser, RuleBodyParser, SourceLocation, Token,
-};
-use std::fmt::{self, Write};
-use style_traits::{CssWriter, ParseError, StyleParseErrorKind, ToCss};
-
-/// A @font-feature-values block declaration.
-/// It is `<ident>: <integer>+`.
-/// This struct can take 3 value types.
-/// - `SingleValue` is to keep just one unsigned integer value.
-/// - `PairValues` is to keep one or two unsigned integer values.
-/// - `VectorValues` is to keep a list of unsigned integer values.
-#[derive(Clone, Debug, PartialEq, ToShmem)]
-pub struct FFVDeclaration<T> {
- /// An `<ident>` for declaration name.
- pub name: Atom,
- /// An `<integer>+` for declaration value.
- pub value: T,
-}
-
-impl<T: ToCss> ToCss for FFVDeclaration<T> {
- fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
- where
- W: Write,
- {
- serialize_atom_identifier(&self.name, dest)?;
- dest.write_str(": ")?;
- self.value.to_css(dest)?;
- dest.write_char(';')
- }
-}
-
-/// A trait for @font-feature-values rule to gecko values conversion.
-#[cfg(feature = "gecko")]
-pub trait ToGeckoFontFeatureValues {
- /// Sets the equivalent of declaration to gecko `nsTArray<u32>` array.
- fn to_gecko_font_feature_values(&self, array: &mut nsTArray<u32>);
-}
-
-/// A @font-feature-values block declaration value that keeps one value.
-#[derive(Clone, Debug, PartialEq, ToCss, ToShmem)]
-pub struct SingleValue(pub u32);
-
-impl Parse for SingleValue {
- fn parse<'i, 't>(
- _context: &ParserContext,
- input: &mut Parser<'i, 't>,
- ) -> Result<SingleValue, ParseError<'i>> {
- let location = input.current_source_location();
- match *input.next()? {
- Token::Number {
- int_value: Some(v), ..
- } if v >= 0 => Ok(SingleValue(v as u32)),
- ref t => Err(location.new_unexpected_token_error(t.clone())),
- }
- }
-}
-
-#[cfg(feature = "gecko")]
-impl ToGeckoFontFeatureValues for SingleValue {
- fn to_gecko_font_feature_values(&self, array: &mut nsTArray<u32>) {
- unsafe {
- array.set_len_pod(1);
- }
- array[0] = self.0 as u32;
- }
-}
-
-/// A @font-feature-values block declaration value that keeps one or two values.
-#[derive(Clone, Debug, PartialEq, ToCss, ToShmem)]
-pub struct PairValues(pub u32, pub Option<u32>);
-
-impl Parse for PairValues {
- fn parse<'i, 't>(
- _context: &ParserContext,
- input: &mut Parser<'i, 't>,
- ) -> Result<PairValues, ParseError<'i>> {
- let location = input.current_source_location();
- let first = match *input.next()? {
- Token::Number {
- int_value: Some(a), ..
- } if a >= 0 => a as u32,
- ref t => return Err(location.new_unexpected_token_error(t.clone())),
- };
- let location = input.current_source_location();
- match input.next() {
- Ok(&Token::Number {
- int_value: Some(b), ..
- }) if b >= 0 => Ok(PairValues(first, Some(b as u32))),
- // It can't be anything other than number.
- Ok(t) => Err(location.new_unexpected_token_error(t.clone())),
- // It can be just one value.
- Err(_) => Ok(PairValues(first, None)),
- }
- }
-}
-
-#[cfg(feature = "gecko")]
-impl ToGeckoFontFeatureValues for PairValues {
- fn to_gecko_font_feature_values(&self, array: &mut nsTArray<u32>) {
- let len = if self.1.is_some() { 2 } else { 1 };
-
- unsafe {
- array.set_len_pod(len);
- }
- array[0] = self.0 as u32;
- if let Some(second) = self.1 {
- array[1] = second as u32;
- };
- }
-}
-
-/// A @font-feature-values block declaration value that keeps a list of values.
-#[derive(Clone, Debug, PartialEq, ToCss, ToShmem)]
-pub struct VectorValues(#[css(iterable)] pub Vec<u32>);
-
-impl Parse for VectorValues {
- fn parse<'i, 't>(
- _context: &ParserContext,
- input: &mut Parser<'i, 't>,
- ) -> Result<VectorValues, ParseError<'i>> {
- let mut vec = vec![];
- loop {
- let location = input.current_source_location();
- match input.next() {
- Ok(&Token::Number {
- int_value: Some(a), ..
- }) if a >= 0 => {
- vec.push(a as u32);
- },
- // It can't be anything other than number.
- Ok(t) => return Err(location.new_unexpected_token_error(t.clone())),
- Err(_) => break,
- }
- }
-
- if vec.len() == 0 {
- return Err(input.new_error(BasicParseErrorKind::EndOfInput));
- }
-
- Ok(VectorValues(vec))
- }
-}
-
-#[cfg(feature = "gecko")]
-impl ToGeckoFontFeatureValues for VectorValues {
- fn to_gecko_font_feature_values(&self, array: &mut nsTArray<u32>) {
- array.assign_from_iter_pod(self.0.iter().map(|v| *v));
- }
-}
-
-/// Parses a list of `FamilyName`s.
-pub fn parse_family_name_list<'i, 't>(
- context: &ParserContext,
- input: &mut Parser<'i, 't>,
-) -> Result<Vec<FamilyName>, ParseError<'i>> {
- input
- .parse_comma_separated(|i| FamilyName::parse(context, i))
- .map_err(|e| e.into())
-}
-
-/// @font-feature-values inside block parser. Parses a list of `FFVDeclaration`.
-/// (`<ident>: <integer>+`)
-struct FFVDeclarationsParser<'a, 'b: 'a, T: 'a> {
- context: &'a ParserContext<'b>,
- declarations: &'a mut Vec<FFVDeclaration<T>>,
-}
-
-/// Default methods reject all at rules.
-impl<'a, 'b, 'i, T> AtRuleParser<'i> for FFVDeclarationsParser<'a, 'b, T> {
- type Prelude = ();
- type AtRule = ();
- type Error = StyleParseErrorKind<'i>;
-}
-
-impl<'a, 'b, 'i, T> QualifiedRuleParser<'i> for FFVDeclarationsParser<'a, 'b, T> {
- type Prelude = ();
- type QualifiedRule = ();
- type Error = StyleParseErrorKind<'i>;
-}
-
-impl<'a, 'b, 'i, T> DeclarationParser<'i> for FFVDeclarationsParser<'a, 'b, T>
-where
- T: Parse,
-{
- type Declaration = ();
- type Error = StyleParseErrorKind<'i>;
-
- fn parse_value<'t>(
- &mut self,
- name: CowRcStr<'i>,
- input: &mut Parser<'i, 't>,
- ) -> Result<(), ParseError<'i>> {
- let value = input.parse_entirely(|i| T::parse(self.context, i))?;
- let new = FFVDeclaration {
- name: Atom::from(&*name),
- value,
- };
- update_or_push(&mut self.declarations, new);
- Ok(())
- }
-}
-
-impl<'a, 'b, 'i, T> RuleBodyItemParser<'i, (), StyleParseErrorKind<'i>>
- for FFVDeclarationsParser<'a, 'b, T>
-where
- T: Parse,
-{
- fn parse_declarations(&self) -> bool {
- true
- }
- fn parse_qualified(&self) -> bool {
- false
- }
-}
-
-macro_rules! font_feature_values_blocks {
- (
- blocks = [
- $( #[$doc: meta] $name: tt $ident: ident / $ident_camel: ident / $gecko_enum: ident: $ty: ty, )*
- ]
- ) => {
- /// The [`@font-feature-values`][font-feature-values] at-rule.
- ///
- /// [font-feature-values]: https://drafts.csswg.org/css-fonts-3/#at-font-feature-values-rule
- #[derive(Clone, Debug, PartialEq, ToShmem)]
- pub struct FontFeatureValuesRule {
- /// Font family list for @font-feature-values rule.
- /// Family names cannot contain generic families. FamilyName
- /// also accepts only non-generic names.
- pub family_names: Vec<FamilyName>,
- $(
- #[$doc]
- pub $ident: Vec<FFVDeclaration<$ty>>,
- )*
- /// The line and column of the rule's source code.
- pub source_location: SourceLocation,
- }
-
- impl FontFeatureValuesRule {
- /// Creates an empty FontFeatureValuesRule with given location and family name list.
- fn new(family_names: Vec<FamilyName>, location: SourceLocation) -> Self {
- FontFeatureValuesRule {
- family_names: family_names,
- $(
- $ident: vec![],
- )*
- source_location: location,
- }
- }
-
- /// Parses a `FontFeatureValuesRule`.
- pub fn parse(
- context: &ParserContext,
- input: &mut Parser,
- family_names: Vec<FamilyName>,
- location: SourceLocation,
- ) -> Self {
- let mut rule = FontFeatureValuesRule::new(family_names, location);
- let mut parser = FontFeatureValuesRuleParser {
- context,
- rule: &mut rule,
- };
- let mut iter = RuleBodyParser::new(input, &mut parser);
- while let Some(result) = iter.next() {
- if let Err((error, slice)) = result {
- let location = error.location;
- let error = ContextualParseError::UnsupportedRule(slice, error);
- context.log_css_error(location, error);
- }
- }
- rule
- }
-
- /// Prints inside of `@font-feature-values` block.
- pub fn value_to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
- where
- W: Write,
- {
- $(
- if self.$ident.len() > 0 {
- dest.write_str(concat!("@", $name, " {\n"))?;
- let iter = self.$ident.iter();
- for val in iter {
- val.to_css(dest)?;
- dest.write_str("\n")?
- }
- dest.write_str("}\n")?
- }
- )*
- Ok(())
- }
-
- /// Returns length of all at-rules.
- pub fn len(&self) -> usize {
- let mut len = 0;
- $(
- len += self.$ident.len();
- )*
- len
- }
-
- /// Convert to Gecko gfxFontFeatureValueSet.
- #[cfg(feature = "gecko")]
- pub fn set_at_rules(&self, dest: *mut gfxFontFeatureValueSet) {
- for ref family in self.family_names.iter() {
- let family = family.name.to_ascii_lowercase();
- $(
- if self.$ident.len() > 0 {
- for val in self.$ident.iter() {
- let array = unsafe {
- Gecko_AppendFeatureValueHashEntry(
- dest,
- family.as_ptr(),
- structs::$gecko_enum,
- val.name.as_ptr()
- )
- };
- unsafe {
- val.value.to_gecko_font_feature_values(&mut *array);
- }
- }
- }
- )*
- }
- }
- }
-
- impl ToCssWithGuard for FontFeatureValuesRule {
- fn to_css(&self, _guard: &SharedRwLockReadGuard, dest: &mut CssStringWriter) -> fmt::Result {
- dest.write_str("@font-feature-values ")?;
- self.family_names.to_css(&mut CssWriter::new(dest))?;
- dest.write_str(" {\n")?;
- self.value_to_css(&mut CssWriter::new(dest))?;
- dest.write_char('}')
- }
- }
-
- /// Updates with new value if same `ident` exists, otherwise pushes to the vector.
- fn update_or_push<T>(vec: &mut Vec<FFVDeclaration<T>>, element: FFVDeclaration<T>) {
- if let Some(item) = vec.iter_mut().find(|item| item.name == element.name) {
- item.value = element.value;
- } else {
- vec.push(element);
- }
- }
-
- /// Keeps the information about block type like @swash, @styleset etc.
- enum BlockType {
- $(
- $ident_camel,
- )*
- }
-
- /// Parser for `FontFeatureValuesRule`. Parses all blocks
- /// <feature-type> {
- /// <feature-value-declaration-list>
- /// }
- /// <feature-type> = @stylistic | @historical-forms | @styleset |
- /// @character-variant | @swash | @ornaments | @annotation
- struct FontFeatureValuesRuleParser<'a> {
- context: &'a ParserContext<'a>,
- rule: &'a mut FontFeatureValuesRule,
- }
-
- /// Default methods reject all qualified rules.
- impl<'a, 'i> QualifiedRuleParser<'i> for FontFeatureValuesRuleParser<'a> {
- type Prelude = ();
- type QualifiedRule = ();
- type Error = StyleParseErrorKind<'i>;
- }
-
- impl<'a, 'i> AtRuleParser<'i> for FontFeatureValuesRuleParser<'a> {
- type Prelude = BlockType;
- type AtRule = ();
- type Error = StyleParseErrorKind<'i>;
-
- fn parse_prelude<'t>(
- &mut self,
- name: CowRcStr<'i>,
- input: &mut Parser<'i, 't>,
- ) -> Result<BlockType, ParseError<'i>> {
- match_ignore_ascii_case! { &*name,
- $(
- $name => Ok(BlockType::$ident_camel),
- )*
- _ => Err(input.new_error(BasicParseErrorKind::AtRuleBodyInvalid)),
- }
- }
-
- fn parse_block<'t>(
- &mut self,
- prelude: BlockType,
- _: &ParserState,
- input: &mut Parser<'i, 't>
- ) -> Result<Self::AtRule, ParseError<'i>> {
- debug_assert!(self.context.rule_types().contains(CssRuleType::FontFeatureValues));
- match prelude {
- $(
- BlockType::$ident_camel => {
- let mut parser = FFVDeclarationsParser {
- context: &self.context,
- declarations: &mut self.rule.$ident,
- };
-
- let mut iter = RuleBodyParser::new(input, &mut parser);
- while let Some(declaration) = iter.next() {
- if let Err((error, slice)) = declaration {
- let location = error.location;
- let error = ContextualParseError::UnsupportedKeyframePropertyDeclaration(
- slice, error
- );
- self.context.log_css_error(location, error);
- }
- }
- },
- )*
- }
-
- Ok(())
- }
- }
-
- impl<'a, 'i> DeclarationParser<'i> for FontFeatureValuesRuleParser<'a> {
- type Declaration = ();
- type Error = StyleParseErrorKind<'i>;
- }
-
- impl<'a, 'i> RuleBodyItemParser<'i, (), StyleParseErrorKind<'i>> for FontFeatureValuesRuleParser<'a> {
- fn parse_declarations(&self) -> bool { false }
- fn parse_qualified(&self) -> bool { true }
- }
- }
-}
-
-font_feature_values_blocks! {
- blocks = [
- #[doc = "A @swash blocksck. \
- Specifies a feature name that will work with the swash() \
- functional notation of font-variant-alternates."]
- "swash" swash / Swash / NS_FONT_VARIANT_ALTERNATES_SWASH: SingleValue,
-
- #[doc = "A @stylistic block. \
- Specifies a feature name that will work with the annotation() \
- functional notation of font-variant-alternates."]
- "stylistic" stylistic / Stylistic / NS_FONT_VARIANT_ALTERNATES_STYLISTIC: SingleValue,
-
- #[doc = "A @ornaments block. \
- Specifies a feature name that will work with the ornaments() ] \
- functional notation of font-variant-alternates."]
- "ornaments" ornaments / Ornaments / NS_FONT_VARIANT_ALTERNATES_ORNAMENTS: SingleValue,
-
- #[doc = "A @annotation block. \
- Specifies a feature name that will work with the stylistic() \
- functional notation of font-variant-alternates."]
- "annotation" annotation / Annotation / NS_FONT_VARIANT_ALTERNATES_ANNOTATION: SingleValue,
-
- #[doc = "A @character-variant block. \
- Specifies a feature name that will work with the styleset() \
- functional notation of font-variant-alternates. The value can be a pair."]
- "character-variant" character_variant / CharacterVariant / NS_FONT_VARIANT_ALTERNATES_CHARACTER_VARIANT:
- PairValues,
-
- #[doc = "A @styleset block. \
- Specifies a feature name that will work with the character-variant() \
- functional notation of font-variant-alternates. The value can be a list."]
- "styleset" styleset / Styleset / NS_FONT_VARIANT_ALTERNATES_STYLESET: VectorValues,
- ]
-}
diff --git a/components/style/stylesheets/font_palette_values_rule.rs b/components/style/stylesheets/font_palette_values_rule.rs
deleted file mode 100644
index c604f9b3c6e..00000000000
--- a/components/style/stylesheets/font_palette_values_rule.rs
+++ /dev/null
@@ -1,268 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
-
-//! The [`@font-palette-values`][font-palette-values] at-rule.
-//!
-//! [font-palette-values]: https://drafts.csswg.org/css-fonts/#font-palette-values
-
-use crate::error_reporting::ContextualParseError;
-#[cfg(feature = "gecko")]
-use crate::gecko_bindings::{
- bindings::Gecko_AppendPaletteValueHashEntry,
- bindings::{Gecko_SetFontPaletteBase, Gecko_SetFontPaletteOverride},
- structs::gfx::FontPaletteValueSet,
- structs::gfx::FontPaletteValueSet_PaletteValues_kDark,
- structs::gfx::FontPaletteValueSet_PaletteValues_kLight,
-};
-use crate::parser::{Parse, ParserContext};
-use crate::shared_lock::{SharedRwLockReadGuard, ToCssWithGuard};
-use crate::str::CssStringWriter;
-use crate::stylesheets::font_feature_values_rule::parse_family_name_list;
-use crate::values::computed::font::FamilyName;
-use crate::values::specified::Color as SpecifiedColor;
-use crate::values::specified::NonNegativeInteger;
-use crate::values::DashedIdent;
-use cssparser::{
- AtRuleParser, CowRcStr, DeclarationParser, Parser, QualifiedRuleParser, RuleBodyItemParser,
- RuleBodyParser, SourceLocation,
-};
-use selectors::parser::SelectorParseErrorKind;
-use std::fmt::{self, Write};
-use style_traits::{Comma, OneOrMoreSeparated};
-use style_traits::{CssWriter, ParseError, StyleParseErrorKind, ToCss};
-
-#[allow(missing_docs)]
-#[derive(Clone, Debug, MallocSizeOf, PartialEq, ToShmem)]
-pub struct FontPaletteOverrideColor {
- index: NonNegativeInteger,
- color: SpecifiedColor,
-}
-
-impl Parse for FontPaletteOverrideColor {
- fn parse<'i, 't>(
- context: &ParserContext,
- input: &mut Parser<'i, 't>,
- ) -> Result<FontPaletteOverrideColor, ParseError<'i>> {
- let index = NonNegativeInteger::parse(context, input)?;
- let location = input.current_source_location();
- let color = SpecifiedColor::parse(context, input)?;
- // Only absolute colors are accepted here.
- if let SpecifiedColor::Absolute { .. } = color {
- Ok(FontPaletteOverrideColor { index, color })
- } else {
- Err(location.new_custom_error(StyleParseErrorKind::UnspecifiedError))
- }
- }
-}
-
-impl ToCss for FontPaletteOverrideColor {
- fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
- where
- W: fmt::Write,
- {
- self.index.to_css(dest)?;
- dest.write_char(' ')?;
- self.color.to_css(dest)
- }
-}
-
-impl OneOrMoreSeparated for FontPaletteOverrideColor {
- type S = Comma;
-}
-
-impl OneOrMoreSeparated for FamilyName {
- type S = Comma;
-}
-
-#[allow(missing_docs)]
-#[derive(Clone, Debug, MallocSizeOf, Parse, PartialEq, ToCss, ToShmem)]
-pub enum FontPaletteBase {
- Light,
- Dark,
- Index(NonNegativeInteger),
-}
-
-/// The [`@font-palette-values`][font-palette-values] at-rule.
-///
-/// [font-palette-values]: https://drafts.csswg.org/css-fonts/#font-palette-values
-#[derive(Clone, Debug, PartialEq, ToShmem)]
-pub struct FontPaletteValuesRule {
- /// Palette name.
- pub name: DashedIdent,
- /// Font family list for @font-palette-values rule.
- /// Family names cannot contain generic families. FamilyName
- /// also accepts only non-generic names.
- pub family_names: Vec<FamilyName>,
- /// The base palette.
- pub base_palette: Option<FontPaletteBase>,
- /// The list of override colors.
- pub override_colors: Vec<FontPaletteOverrideColor>,
- /// The line and column of the rule's source code.
- pub source_location: SourceLocation,
-}
-
-impl FontPaletteValuesRule {
- /// Creates an empty FontPaletteValuesRule with given location and name.
- fn new(name: DashedIdent, location: SourceLocation) -> Self {
- FontPaletteValuesRule {
- name,
- family_names: vec![],
- base_palette: None,
- override_colors: vec![],
- source_location: location,
- }
- }
-
- /// Parses a `FontPaletteValuesRule`.
- pub fn parse(
- context: &ParserContext,
- input: &mut Parser,
- name: DashedIdent,
- location: SourceLocation,
- ) -> Self {
- let mut rule = FontPaletteValuesRule::new(name, location);
- let mut parser = FontPaletteValuesDeclarationParser {
- context,
- rule: &mut rule,
- };
- let mut iter = RuleBodyParser::new(input, &mut parser);
- while let Some(declaration) = iter.next() {
- if let Err((error, slice)) = declaration {
- let location = error.location;
- let error =
- ContextualParseError::UnsupportedFontPaletteValuesDescriptor(slice, error);
- context.log_css_error(location, error);
- }
- }
- rule
- }
-
- /// Prints inside of `@font-palette-values` block.
- fn value_to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
- where
- W: Write,
- {
- if !self.family_names.is_empty() {
- dest.write_str("font-family: ")?;
- self.family_names.to_css(dest)?;
- dest.write_str("; ")?;
- }
- if let Some(base) = &self.base_palette {
- dest.write_str("base-palette: ")?;
- base.to_css(dest)?;
- dest.write_str("; ")?;
- }
- if !self.override_colors.is_empty() {
- dest.write_str("override-colors: ")?;
- self.override_colors.to_css(dest)?;
- dest.write_str("; ")?;
- }
- Ok(())
- }
-
- /// Convert to Gecko FontPaletteValueSet.
- #[cfg(feature = "gecko")]
- pub fn to_gecko_palette_value_set(&self, dest: *mut FontPaletteValueSet) {
- for ref family in self.family_names.iter() {
- let family = family.name.to_ascii_lowercase();
- let palette_values = unsafe {
- Gecko_AppendPaletteValueHashEntry(dest, family.as_ptr(), self.name.0.as_ptr())
- };
- if let Some(base_palette) = &self.base_palette {
- unsafe {
- Gecko_SetFontPaletteBase(
- palette_values,
- match &base_palette {
- FontPaletteBase::Light => FontPaletteValueSet_PaletteValues_kLight,
- FontPaletteBase::Dark => FontPaletteValueSet_PaletteValues_kDark,
- FontPaletteBase::Index(i) => i.0.value() as i32,
- },
- );
- }
- }
- for c in &self.override_colors {
- if let SpecifiedColor::Absolute(ref absolute) = c.color {
- unsafe {
- Gecko_SetFontPaletteOverride(
- palette_values,
- c.index.0.value(),
- (&absolute.color) as *const _ as *mut _,
- );
- }
- }
- }
- }
- }
-}
-
-impl ToCssWithGuard for FontPaletteValuesRule {
- fn to_css(&self, _guard: &SharedRwLockReadGuard, dest: &mut CssStringWriter) -> fmt::Result {
- dest.write_str("@font-palette-values ")?;
- self.name.to_css(&mut CssWriter::new(dest))?;
- dest.write_str(" { ")?;
- self.value_to_css(&mut CssWriter::new(dest))?;
- dest.write_char('}')
- }
-}
-
-/// Parser for declarations in `FontPaletteValuesRule`.
-struct FontPaletteValuesDeclarationParser<'a> {
- context: &'a ParserContext<'a>,
- rule: &'a mut FontPaletteValuesRule,
-}
-
-impl<'a, 'i> AtRuleParser<'i> for FontPaletteValuesDeclarationParser<'a> {
- type Prelude = ();
- type AtRule = ();
- type Error = StyleParseErrorKind<'i>;
-}
-
-impl<'a, 'i> QualifiedRuleParser<'i> for FontPaletteValuesDeclarationParser<'a> {
- type Prelude = ();
- type QualifiedRule = ();
- type Error = StyleParseErrorKind<'i>;
-}
-
-fn parse_override_colors<'i, 't>(
- context: &ParserContext,
- input: &mut Parser<'i, 't>,
-) -> Result<Vec<FontPaletteOverrideColor>, ParseError<'i>> {
- input.parse_comma_separated(|i| FontPaletteOverrideColor::parse(context, i))
-}
-
-impl<'a, 'b, 'i> DeclarationParser<'i> for FontPaletteValuesDeclarationParser<'a> {
- type Declaration = ();
- type Error = StyleParseErrorKind<'i>;
-
- fn parse_value<'t>(
- &mut self,
- name: CowRcStr<'i>,
- input: &mut Parser<'i, 't>,
- ) -> Result<(), ParseError<'i>> {
- match_ignore_ascii_case! { &*name,
- "font-family" => {
- self.rule.family_names = parse_family_name_list(self.context, input)?
- },
- "base-palette" => {
- self.rule.base_palette = Some(input.parse_entirely(|i| FontPaletteBase::parse(self.context, i))?)
- },
- "override-colors" => {
- self.rule.override_colors = parse_override_colors(self.context, input)?
- },
- _ => return Err(input.new_custom_error(SelectorParseErrorKind::UnexpectedIdent(name.clone()))),
- }
- Ok(())
- }
-}
-
-impl<'a, 'i> RuleBodyItemParser<'i, (), StyleParseErrorKind<'i>>
- for FontPaletteValuesDeclarationParser<'a>
-{
- fn parse_declarations(&self) -> bool {
- true
- }
- fn parse_qualified(&self) -> bool {
- false
- }
-}
diff --git a/components/style/stylesheets/import_rule.rs b/components/style/stylesheets/import_rule.rs
deleted file mode 100644
index e7ea2748465..00000000000
--- a/components/style/stylesheets/import_rule.rs
+++ /dev/null
@@ -1,332 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
-
-//! The [`@import`][import] at-rule.
-//!
-//! [import]: https://drafts.csswg.org/css-cascade-3/#at-import
-
-use crate::media_queries::MediaList;
-use crate::parser::{Parse, ParserContext};
-use crate::shared_lock::{
- DeepCloneParams, DeepCloneWithLock, SharedRwLock, SharedRwLockReadGuard, ToCssWithGuard,
-};
-use crate::str::CssStringWriter;
-use crate::stylesheets::{
- layer_rule::LayerName, supports_rule::SupportsCondition, CssRule, CssRuleType,
- StylesheetInDocument,
-};
-use crate::values::CssUrl;
-use cssparser::{Parser, SourceLocation};
-use std::fmt::{self, Write};
-use style_traits::{CssWriter, ToCss};
-use to_shmem::{self, SharedMemoryBuilder, ToShmem};
-
-/// A sheet that is held from an import rule.
-#[cfg(feature = "gecko")]
-#[derive(Debug)]
-pub enum ImportSheet {
- /// A bonafide stylesheet.
- Sheet(crate::gecko::data::GeckoStyleSheet),
-
- /// An @import created while parsing off-main-thread, whose Gecko sheet has
- /// yet to be created and attached.
- Pending,
-
- /// An @import created with a false <supports-condition>, so will never be fetched.
- Refused,
-}
-
-#[cfg(feature = "gecko")]
-impl ImportSheet {
- /// Creates a new ImportSheet from a GeckoStyleSheet.
- pub fn new(sheet: crate::gecko::data::GeckoStyleSheet) -> Self {
- ImportSheet::Sheet(sheet)
- }
-
- /// Creates a pending ImportSheet for a load that has not started yet.
- pub fn new_pending() -> Self {
- ImportSheet::Pending
- }
-
- /// Creates a refused ImportSheet for a load that will not happen.
- pub fn new_refused() -> Self {
- ImportSheet::Refused
- }
-
- /// Returns a reference to the GeckoStyleSheet in this ImportSheet, if it
- /// exists.
- pub fn as_sheet(&self) -> Option<&crate::gecko::data::GeckoStyleSheet> {
- match *self {
- ImportSheet::Sheet(ref s) => {
- debug_assert!(!s.hack_is_null());
- if s.hack_is_null() {
- return None;
- }
- Some(s)
- },
- ImportSheet::Refused | ImportSheet::Pending => None,
- }
- }
-
- /// Returns the media list for this import rule.
- pub fn media<'a>(&'a self, guard: &'a SharedRwLockReadGuard) -> Option<&'a MediaList> {
- self.as_sheet().and_then(|s| s.media(guard))
- }
-
- /// Returns the rule list for this import rule.
- pub fn rules<'a>(&'a self, guard: &'a SharedRwLockReadGuard) -> &'a [CssRule] {
- match self.as_sheet() {
- Some(s) => s.rules(guard),
- None => &[],
- }
- }
-}
-
-#[cfg(feature = "gecko")]
-impl DeepCloneWithLock for ImportSheet {
- fn deep_clone_with_lock(
- &self,
- _lock: &SharedRwLock,
- _guard: &SharedRwLockReadGuard,
- params: &DeepCloneParams,
- ) -> Self {
- use crate::gecko::data::GeckoStyleSheet;
- use crate::gecko_bindings::bindings;
- match *self {
- ImportSheet::Sheet(ref s) => {
- let clone = unsafe {
- bindings::Gecko_StyleSheet_Clone(s.raw() as *const _, params.reference_sheet)
- };
- ImportSheet::Sheet(unsafe { GeckoStyleSheet::from_addrefed(clone) })
- },
- ImportSheet::Pending => ImportSheet::Pending,
- ImportSheet::Refused => ImportSheet::Refused,
- }
- }
-}
-
-/// A sheet that is held from an import rule.
-#[cfg(feature = "servo")]
-#[derive(Debug)]
-pub enum ImportSheet {
- /// A bonafide stylesheet.
- Sheet(::servo_arc::Arc<crate::stylesheets::Stylesheet>),
-
- /// An @import created with a false <supports-condition>, so will never be fetched.
- Refused,
-}
-
-#[cfg(feature = "servo")]
-impl ImportSheet {
- /// Creates a new ImportSheet from a stylesheet.
- pub fn new(sheet: ::servo_arc::Arc<crate::stylesheets::Stylesheet>) -> Self {
- ImportSheet::Sheet(sheet)
- }
-
- /// Creates a refused ImportSheet for a load that will not happen.
- pub fn new_refused() -> Self {
- ImportSheet::Refused
- }
-
- /// Returns a reference to the stylesheet in this ImportSheet, if it exists.
- pub fn as_sheet(&self) -> Option<&::servo_arc::Arc<crate::stylesheets::Stylesheet>> {
- match *self {
- ImportSheet::Sheet(ref s) => Some(s),
- ImportSheet::Refused => None,
- }
- }
-
- /// Returns the media list for this import rule.
- pub fn media<'a>(&'a self, guard: &'a SharedRwLockReadGuard) -> Option<&'a MediaList> {
- self.as_sheet().and_then(|s| s.media(guard))
- }
-
- /// Returns the rules for this import rule.
- pub fn rules<'a>(&'a self, guard: &'a SharedRwLockReadGuard) -> &'a [CssRule] {
- match self.as_sheet() {
- Some(s) => s.rules(guard),
- None => &[],
- }
- }
-}
-
-#[cfg(feature = "servo")]
-impl DeepCloneWithLock for ImportSheet {
- fn deep_clone_with_lock(
- &self,
- _lock: &SharedRwLock,
- _guard: &SharedRwLockReadGuard,
- _params: &DeepCloneParams,
- ) -> Self {
- match *self {
- ImportSheet::Sheet(ref s) => {
- use servo_arc::Arc;
- ImportSheet::Sheet(Arc::new((&**s).clone()))
- },
- ImportSheet::Refused => ImportSheet::Refused,
- }
- }
-}
-
-/// The layer specified in an import rule (can be none, anonymous, or named).
-#[derive(Debug, Clone)]
-pub enum ImportLayer {
- /// No layer specified
- None,
-
- /// Anonymous layer (`layer`)
- Anonymous,
-
- /// Named layer (`layer(name)`)
- Named(LayerName),
-}
-
-/// The supports condition in an import rule.
-#[derive(Debug, Clone)]
-pub struct ImportSupportsCondition {
- /// The supports condition.
- pub condition: SupportsCondition,
-
- /// If the import is enabled, from the result of the import condition.
- pub enabled: bool,
-}
-
-impl ToCss for ImportLayer {
- fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
- where
- W: Write,
- {
- match *self {
- ImportLayer::None => Ok(()),
- ImportLayer::Anonymous => dest.write_str("layer"),
- ImportLayer::Named(ref name) => {
- dest.write_str("layer(")?;
- name.to_css(dest)?;
- dest.write_char(')')
- },
- }
- }
-}
-
-/// The [`@import`][import] at-rule.
-///
-/// [import]: https://drafts.csswg.org/css-cascade-3/#at-import
-#[derive(Debug)]
-pub struct ImportRule {
- /// The `<url>` this `@import` rule is loading.
- pub url: CssUrl,
-
- /// The stylesheet is always present. However, in the case of gecko async
- /// parsing, we don't actually have a Gecko sheet at first, and so the
- /// ImportSheet just has stub behavior until it appears.
- pub stylesheet: ImportSheet,
-
- /// A <supports-condition> for the rule.
- pub supports: Option<ImportSupportsCondition>,
-
- /// A `layer()` function name.
- pub layer: ImportLayer,
-
- /// The line and column of the rule's source code.
- pub source_location: SourceLocation,
-}
-
-impl ImportRule {
- /// Parses the layer() / layer / supports() part of the import header, as per
- /// https://drafts.csswg.org/css-cascade-5/#at-import:
- ///
- /// [ layer | layer(<layer-name>) ]?
- /// [ supports([ <supports-condition> | <declaration> ]) ]?
- ///
- /// We do this here so that the import preloader can look at this without having to parse the
- /// whole import rule or parse the media query list or what not.
- pub fn parse_layer_and_supports<'i, 't>(
- input: &mut Parser<'i, 't>,
- context: &mut ParserContext,
- ) -> (ImportLayer, Option<ImportSupportsCondition>) {
- let layer = if input
- .try_parse(|input| input.expect_ident_matching("layer"))
- .is_ok()
- {
- ImportLayer::Anonymous
- } else {
- input
- .try_parse(|input| {
- input.expect_function_matching("layer")?;
- input
- .parse_nested_block(|input| LayerName::parse(context, input))
- .map(|name| ImportLayer::Named(name))
- })
- .ok()
- .unwrap_or(ImportLayer::None)
- };
-
- let supports = if !static_prefs::pref!("layout.css.import-supports.enabled") {
- None
- } else {
- input
- .try_parse(SupportsCondition::parse_for_import)
- .map(|condition| {
- let enabled = context
- .nest_for_rule(CssRuleType::Style, |context| condition.eval(context));
- ImportSupportsCondition { condition, enabled }
- })
- .ok()
- };
-
- (layer, supports)
- }
-}
-
-impl ToShmem for ImportRule {
- fn to_shmem(&self, _builder: &mut SharedMemoryBuilder) -> to_shmem::Result<Self> {
- Err(String::from(
- "ToShmem failed for ImportRule: cannot handle imported style sheets",
- ))
- }
-}
-
-impl DeepCloneWithLock for ImportRule {
- fn deep_clone_with_lock(
- &self,
- lock: &SharedRwLock,
- guard: &SharedRwLockReadGuard,
- params: &DeepCloneParams,
- ) -> Self {
- ImportRule {
- url: self.url.clone(),
- stylesheet: self.stylesheet.deep_clone_with_lock(lock, guard, params),
- supports: self.supports.clone(),
- layer: self.layer.clone(),
- source_location: self.source_location.clone(),
- }
- }
-}
-
-impl ToCssWithGuard for ImportRule {
- fn to_css(&self, guard: &SharedRwLockReadGuard, dest: &mut CssStringWriter) -> fmt::Result {
- dest.write_str("@import ")?;
- self.url.to_css(&mut CssWriter::new(dest))?;
-
- if !matches!(self.layer, ImportLayer::None) {
- dest.write_char(' ')?;
- self.layer.to_css(&mut CssWriter::new(dest))?;
- }
-
- if let Some(ref supports) = self.supports {
- dest.write_str(" supports(")?;
- supports.condition.to_css(&mut CssWriter::new(dest))?;
- dest.write_char(')')?;
- }
-
- if let Some(media) = self.stylesheet.media(guard) {
- if !media.is_empty() {
- dest.write_char(' ')?;
- media.to_css(&mut CssWriter::new(dest))?;
- }
- }
-
- dest.write_char(';')
- }
-}
diff --git a/components/style/stylesheets/keyframes_rule.rs b/components/style/stylesheets/keyframes_rule.rs
deleted file mode 100644
index 6e5016080e9..00000000000
--- a/components/style/stylesheets/keyframes_rule.rs
+++ /dev/null
@@ -1,691 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
-
-//! Keyframes: https://drafts.csswg.org/css-animations/#keyframes
-
-use crate::error_reporting::ContextualParseError;
-use crate::parser::ParserContext;
-use crate::properties::longhands::animation_composition::single_value::SpecifiedValue as SpecifiedComposition;
-use crate::properties::longhands::transition_timing_function::single_value::SpecifiedValue as SpecifiedTimingFunction;
-use crate::properties::LonghandIdSet;
-use crate::properties::{Importance, PropertyDeclaration};
-use crate::properties::{LonghandId, PropertyDeclarationBlock, PropertyId};
-use crate::properties::{PropertyDeclarationId, SourcePropertyDeclaration};
-use crate::shared_lock::{DeepCloneParams, DeepCloneWithLock, SharedRwLock, SharedRwLockReadGuard};
-use crate::shared_lock::{Locked, ToCssWithGuard};
-use crate::str::CssStringWriter;
-use crate::stylesheets::rule_parser::VendorPrefix;
-use crate::stylesheets::{CssRuleType, StylesheetContents};
-use crate::values::{serialize_percentage, KeyframesName};
-use cssparser::{
- parse_one_rule, AtRuleParser, CowRcStr, DeclarationParser, Parser, ParserInput, ParserState,
- QualifiedRuleParser, RuleBodyItemParser, RuleBodyParser, SourceLocation, Token,
-};
-use servo_arc::Arc;
-use std::borrow::Cow;
-use std::fmt::{self, Write};
-use style_traits::{CssWriter, ParseError, ParsingMode, StyleParseErrorKind, ToCss};
-
-/// A [`@keyframes`][keyframes] rule.
-///
-/// [keyframes]: https://drafts.csswg.org/css-animations/#keyframes
-#[derive(Debug, ToShmem)]
-pub struct KeyframesRule {
- /// The name of the current animation.
- pub name: KeyframesName,
- /// The keyframes specified for this CSS rule.
- pub keyframes: Vec<Arc<Locked<Keyframe>>>,
- /// Vendor prefix type the @keyframes has.
- pub vendor_prefix: Option<VendorPrefix>,
- /// The line and column of the rule's source code.
- pub source_location: SourceLocation,
-}
-
-impl ToCssWithGuard for KeyframesRule {
- // Serialization of KeyframesRule is not specced.
- fn to_css(&self, guard: &SharedRwLockReadGuard, dest: &mut CssStringWriter) -> fmt::Result {
- dest.write_str("@keyframes ")?;
- self.name.to_css(&mut CssWriter::new(dest))?;
- dest.write_str(" {")?;
- let iter = self.keyframes.iter();
- for lock in iter {
- dest.write_str("\n")?;
- let keyframe = lock.read_with(&guard);
- keyframe.to_css(guard, dest)?;
- }
- dest.write_str("\n}")
- }
-}
-
-impl KeyframesRule {
- /// Returns the index of the last keyframe that matches the given selector.
- /// If the selector is not valid, or no keyframe is found, returns None.
- ///
- /// Related spec:
- /// <https://drafts.csswg.org/css-animations-1/#interface-csskeyframesrule-findrule>
- pub fn find_rule(&self, guard: &SharedRwLockReadGuard, selector: &str) -> Option<usize> {
- let mut input = ParserInput::new(selector);
- if let Ok(selector) = Parser::new(&mut input).parse_entirely(KeyframeSelector::parse) {
- for (i, keyframe) in self.keyframes.iter().enumerate().rev() {
- if keyframe.read_with(guard).selector == selector {
- return Some(i);
- }
- }
- }
- None
- }
-}
-
-impl DeepCloneWithLock for KeyframesRule {
- fn deep_clone_with_lock(
- &self,
- lock: &SharedRwLock,
- guard: &SharedRwLockReadGuard,
- params: &DeepCloneParams,
- ) -> Self {
- KeyframesRule {
- name: self.name.clone(),
- keyframes: self
- .keyframes
- .iter()
- .map(|x| {
- Arc::new(
- lock.wrap(x.read_with(guard).deep_clone_with_lock(lock, guard, params)),
- )
- })
- .collect(),
- vendor_prefix: self.vendor_prefix.clone(),
- source_location: self.source_location.clone(),
- }
- }
-}
-
-/// A number from 0 to 1, indicating the percentage of the animation when this
-/// keyframe should run.
-#[derive(Clone, Copy, Debug, MallocSizeOf, PartialEq, PartialOrd, ToShmem)]
-pub struct KeyframePercentage(pub f32);
-
-impl ::std::cmp::Ord for KeyframePercentage {
- #[inline]
- fn cmp(&self, other: &Self) -> ::std::cmp::Ordering {
- // We know we have a number from 0 to 1, so unwrap() here is safe.
- self.0.partial_cmp(&other.0).unwrap()
- }
-}
-
-impl ::std::cmp::Eq for KeyframePercentage {}
-
-impl ToCss for KeyframePercentage {
- fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
- where
- W: Write,
- {
- serialize_percentage(self.0, dest)
- }
-}
-
-impl KeyframePercentage {
- /// Trivially constructs a new `KeyframePercentage`.
- #[inline]
- pub fn new(value: f32) -> KeyframePercentage {
- debug_assert!(value >= 0. && value <= 1.);
- KeyframePercentage(value)
- }
-
- fn parse<'i, 't>(input: &mut Parser<'i, 't>) -> Result<KeyframePercentage, ParseError<'i>> {
- let token = input.next()?.clone();
- match token {
- Token::Ident(ref identifier) if identifier.as_ref().eq_ignore_ascii_case("from") => {
- Ok(KeyframePercentage::new(0.))
- },
- Token::Ident(ref identifier) if identifier.as_ref().eq_ignore_ascii_case("to") => {
- Ok(KeyframePercentage::new(1.))
- },
- Token::Percentage {
- unit_value: percentage,
- ..
- } if percentage >= 0. && percentage <= 1. => Ok(KeyframePercentage::new(percentage)),
- _ => Err(input.new_unexpected_token_error(token)),
- }
- }
-}
-
-/// A keyframes selector is a list of percentages or from/to symbols, which are
-/// converted at parse time to percentages.
-#[derive(Clone, Debug, Eq, PartialEq, ToCss, ToShmem)]
-#[css(comma)]
-pub struct KeyframeSelector(#[css(iterable)] Vec<KeyframePercentage>);
-
-impl KeyframeSelector {
- /// Return the list of percentages this selector contains.
- #[inline]
- pub fn percentages(&self) -> &[KeyframePercentage] {
- &self.0
- }
-
- /// A dummy public function so we can write a unit test for this.
- pub fn new_for_unit_testing(percentages: Vec<KeyframePercentage>) -> KeyframeSelector {
- KeyframeSelector(percentages)
- }
-
- /// Parse a keyframe selector from CSS input.
- pub fn parse<'i, 't>(input: &mut Parser<'i, 't>) -> Result<Self, ParseError<'i>> {
- input
- .parse_comma_separated(KeyframePercentage::parse)
- .map(KeyframeSelector)
- }
-}
-
-/// A keyframe.
-#[derive(Debug, ToShmem)]
-pub struct Keyframe {
- /// The selector this keyframe was specified from.
- pub selector: KeyframeSelector,
-
- /// The declaration block that was declared inside this keyframe.
- ///
- /// Note that `!important` rules in keyframes don't apply, but we keep this
- /// `Arc` just for convenience.
- pub block: Arc<Locked<PropertyDeclarationBlock>>,
-
- /// The line and column of the rule's source code.
- pub source_location: SourceLocation,
-}
-
-impl ToCssWithGuard for Keyframe {
- fn to_css(&self, guard: &SharedRwLockReadGuard, dest: &mut CssStringWriter) -> fmt::Result {
- self.selector.to_css(&mut CssWriter::new(dest))?;
- dest.write_str(" { ")?;
- self.block.read_with(guard).to_css(dest)?;
- dest.write_str(" }")?;
- Ok(())
- }
-}
-
-impl Keyframe {
- /// Parse a CSS keyframe.
- pub fn parse<'i>(
- css: &'i str,
- parent_stylesheet_contents: &StylesheetContents,
- lock: &SharedRwLock,
- ) -> Result<Arc<Locked<Self>>, ParseError<'i>> {
- let url_data = parent_stylesheet_contents.url_data.read();
- let namespaces = parent_stylesheet_contents.namespaces.read();
- let mut context = ParserContext::new(
- parent_stylesheet_contents.origin,
- &url_data,
- Some(CssRuleType::Keyframe),
- ParsingMode::DEFAULT,
- parent_stylesheet_contents.quirks_mode,
- Cow::Borrowed(&*namespaces),
- None,
- None,
- );
- let mut input = ParserInput::new(css);
- let mut input = Parser::new(&mut input);
-
- let mut declarations = SourcePropertyDeclaration::default();
- let mut rule_parser = KeyframeListParser {
- context: &mut context,
- shared_lock: &lock,
- declarations: &mut declarations,
- };
- parse_one_rule(&mut input, &mut rule_parser)
- }
-}
-
-impl DeepCloneWithLock for Keyframe {
- /// Deep clones this Keyframe.
- fn deep_clone_with_lock(
- &self,
- lock: &SharedRwLock,
- guard: &SharedRwLockReadGuard,
- _params: &DeepCloneParams,
- ) -> Keyframe {
- Keyframe {
- selector: self.selector.clone(),
- block: Arc::new(lock.wrap(self.block.read_with(guard).clone())),
- source_location: self.source_location.clone(),
- }
- }
-}
-
-/// A keyframes step value. This can be a synthetised keyframes animation, that
-/// is, one autogenerated from the current computed values, or a list of
-/// declarations to apply.
-///
-/// TODO: Find a better name for this?
-#[derive(Clone, Debug, MallocSizeOf)]
-pub enum KeyframesStepValue {
- /// A step formed by a declaration block specified by the CSS.
- Declarations {
- /// The declaration block per se.
- #[cfg_attr(
- feature = "gecko",
- ignore_malloc_size_of = "XXX: Primary ref, measure if DMD says it's worthwhile"
- )]
- #[cfg_attr(feature = "servo", ignore_malloc_size_of = "Arc")]
- block: Arc<Locked<PropertyDeclarationBlock>>,
- },
- /// A synthetic step computed from the current computed values at the time
- /// of the animation.
- ComputedValues,
-}
-
-/// A single step from a keyframe animation.
-#[derive(Clone, Debug, MallocSizeOf)]
-pub struct KeyframesStep {
- /// The percentage of the animation duration when this step starts.
- pub start_percentage: KeyframePercentage,
- /// Declarations that will determine the final style during the step, or
- /// `ComputedValues` if this is an autogenerated step.
- pub value: KeyframesStepValue,
- /// Whether an animation-timing-function declaration exists in the list of
- /// declarations.
- ///
- /// This is used to know when to override the keyframe animation style.
- pub declared_timing_function: bool,
- /// Whether an animation-composition declaration exists in the list of
- /// declarations.
- ///
- /// This is used to know when to override the keyframe animation style.
- pub declared_composition: bool,
-}
-
-impl KeyframesStep {
- #[inline]
- fn new(
- start_percentage: KeyframePercentage,
- value: KeyframesStepValue,
- guard: &SharedRwLockReadGuard,
- ) -> Self {
- let mut declared_timing_function = false;
- let mut declared_composition = false;
- if let KeyframesStepValue::Declarations { ref block } = value {
- for prop_decl in block.read_with(guard).declarations().iter() {
- match *prop_decl {
- PropertyDeclaration::AnimationTimingFunction(..) => {
- declared_timing_function = true;
- },
- PropertyDeclaration::AnimationComposition(..) => {
- declared_composition = true;
- },
- _ => continue,
- }
- // Don't need to continue the loop if both are found.
- if declared_timing_function && declared_composition {
- break;
- }
- }
- }
-
- KeyframesStep {
- start_percentage,
- value,
- declared_timing_function,
- declared_composition,
- }
- }
-
- /// Return specified PropertyDeclaration.
- #[inline]
- fn get_declared_property<'a>(
- &'a self,
- guard: &'a SharedRwLockReadGuard,
- property: LonghandId,
- ) -> Option<&'a PropertyDeclaration> {
- match self.value {
- KeyframesStepValue::Declarations { ref block } => {
- let guard = block.read_with(guard);
- let (declaration, _) = guard
- .get(PropertyDeclarationId::Longhand(property))
- .unwrap();
- match *declaration {
- PropertyDeclaration::CSSWideKeyword(..) => None,
- // FIXME: Bug 1710735: Support css variable in @keyframes rule.
- PropertyDeclaration::WithVariables(..) => None,
- _ => Some(declaration),
- }
- },
- KeyframesStepValue::ComputedValues => {
- panic!("Shouldn't happen to set this property in missing keyframes")
- },
- }
- }
-
- /// Return specified TransitionTimingFunction if this KeyframesSteps has
- /// 'animation-timing-function'.
- pub fn get_animation_timing_function(
- &self,
- guard: &SharedRwLockReadGuard,
- ) -> Option<SpecifiedTimingFunction> {
- if !self.declared_timing_function {
- return None;
- }
-
- self.get_declared_property(guard, LonghandId::AnimationTimingFunction)
- .map(|decl| {
- match *decl {
- PropertyDeclaration::AnimationTimingFunction(ref value) => {
- // Use the first value
- value.0[0].clone()
- },
- _ => unreachable!("Unexpected PropertyDeclaration"),
- }
- })
- }
-
- /// Return CompositeOperation if this KeyframesSteps has 'animation-composition'.
- pub fn get_animation_composition(
- &self,
- guard: &SharedRwLockReadGuard,
- ) -> Option<SpecifiedComposition> {
- if !self.declared_composition {
- return None;
- }
-
- self.get_declared_property(guard, LonghandId::AnimationComposition)
- .map(|decl| {
- match *decl {
- PropertyDeclaration::AnimationComposition(ref value) => {
- // Use the first value
- value.0[0].clone()
- },
- _ => unreachable!("Unexpected PropertyDeclaration"),
- }
- })
- }
-}
-
-/// This structure represents a list of animation steps computed from the list
-/// of keyframes, in order.
-///
-/// It only takes into account animable properties.
-#[derive(Clone, Debug, MallocSizeOf)]
-pub struct KeyframesAnimation {
- /// The difference steps of the animation.
- pub steps: Vec<KeyframesStep>,
- /// The properties that change in this animation.
- pub properties_changed: LonghandIdSet,
- /// Vendor prefix type the @keyframes has.
- pub vendor_prefix: Option<VendorPrefix>,
-}
-
-/// Get all the animated properties in a keyframes animation.
-fn get_animated_properties(
- keyframes: &[Arc<Locked<Keyframe>>],
- guard: &SharedRwLockReadGuard,
-) -> LonghandIdSet {
- let mut ret = LonghandIdSet::new();
- // NB: declarations are already deduplicated, so we don't have to check for
- // it here.
- for keyframe in keyframes {
- let keyframe = keyframe.read_with(&guard);
- let block = keyframe.block.read_with(guard);
- // CSS Animations spec clearly defines that properties with !important
- // in keyframe rules are invalid and ignored, but it's still ambiguous
- // whether we should drop the !important properties or retain the
- // properties when they are set via CSSOM. So we assume there might
- // be properties with !important in keyframe rules here.
- // See the spec issue https://github.com/w3c/csswg-drafts/issues/1824
- for declaration in block.normal_declaration_iter() {
- let longhand_id = match declaration.id() {
- PropertyDeclarationId::Longhand(id) => id,
- _ => continue,
- };
-
- if longhand_id == LonghandId::Display {
- continue;
- }
-
- if !longhand_id.is_animatable() {
- continue;
- }
-
- ret.insert(longhand_id);
- }
- }
-
- ret
-}
-
-impl KeyframesAnimation {
- /// Create a keyframes animation from a given list of keyframes.
- ///
- /// This will return a keyframe animation with empty steps and
- /// properties_changed if the list of keyframes is empty, or there are no
- /// animated properties obtained from the keyframes.
- ///
- /// Otherwise, this will compute and sort the steps used for the animation,
- /// and return the animation object.
- pub fn from_keyframes(
- keyframes: &[Arc<Locked<Keyframe>>],
- vendor_prefix: Option<VendorPrefix>,
- guard: &SharedRwLockReadGuard,
- ) -> Self {
- let mut result = KeyframesAnimation {
- steps: vec![],
- properties_changed: LonghandIdSet::new(),
- vendor_prefix,
- };
-
- if keyframes.is_empty() {
- return result;
- }
-
- result.properties_changed = get_animated_properties(keyframes, guard);
- if result.properties_changed.is_empty() {
- return result;
- }
-
- for keyframe in keyframes {
- let keyframe = keyframe.read_with(&guard);
- for percentage in keyframe.selector.0.iter() {
- result.steps.push(KeyframesStep::new(
- *percentage,
- KeyframesStepValue::Declarations {
- block: keyframe.block.clone(),
- },
- guard,
- ));
- }
- }
-
- // Sort by the start percentage, so we can easily find a frame.
- result.steps.sort_by_key(|step| step.start_percentage);
-
- // Prepend autogenerated keyframes if appropriate.
- if result.steps[0].start_percentage.0 != 0. {
- result.steps.insert(
- 0,
- KeyframesStep::new(
- KeyframePercentage::new(0.),
- KeyframesStepValue::ComputedValues,
- guard,
- ),
- );
- }
-
- if result.steps.last().unwrap().start_percentage.0 != 1. {
- result.steps.push(KeyframesStep::new(
- KeyframePercentage::new(1.),
- KeyframesStepValue::ComputedValues,
- guard,
- ));
- }
-
- result
- }
-}
-
-/// Parses a keyframes list, like:
-/// 0%, 50% {
-/// width: 50%;
-/// }
-///
-/// 40%, 60%, 100% {
-/// width: 100%;
-/// }
-struct KeyframeListParser<'a, 'b> {
- context: &'a mut ParserContext<'b>,
- shared_lock: &'a SharedRwLock,
- declarations: &'a mut SourcePropertyDeclaration,
-}
-
-/// Parses a keyframe list from CSS input.
-pub fn parse_keyframe_list<'a>(
- context: &mut ParserContext<'a>,
- input: &mut Parser,
- shared_lock: &SharedRwLock,
-) -> Vec<Arc<Locked<Keyframe>>> {
- let mut declarations = SourcePropertyDeclaration::default();
- let mut parser = KeyframeListParser {
- context,
- shared_lock,
- declarations: &mut declarations,
- };
- RuleBodyParser::new(input, &mut parser)
- .filter_map(Result::ok)
- .collect()
-}
-
-impl<'a, 'b, 'i> AtRuleParser<'i> for KeyframeListParser<'a, 'b> {
- type Prelude = ();
- type AtRule = Arc<Locked<Keyframe>>;
- type Error = StyleParseErrorKind<'i>;
-}
-
-impl<'a, 'b, 'i> DeclarationParser<'i> for KeyframeListParser<'a, 'b> {
- type Declaration = Arc<Locked<Keyframe>>;
- type Error = StyleParseErrorKind<'i>;
-}
-
-impl<'a, 'b, 'i> QualifiedRuleParser<'i> for KeyframeListParser<'a, 'b> {
- type Prelude = KeyframeSelector;
- type QualifiedRule = Arc<Locked<Keyframe>>;
- type Error = StyleParseErrorKind<'i>;
-
- fn parse_prelude<'t>(
- &mut self,
- input: &mut Parser<'i, 't>,
- ) -> Result<Self::Prelude, ParseError<'i>> {
- let start_position = input.position();
- KeyframeSelector::parse(input).map_err(|e| {
- let location = e.location;
- let error = ContextualParseError::InvalidKeyframeRule(
- input.slice_from(start_position),
- e.clone(),
- );
- self.context.log_css_error(location, error);
- e
- })
- }
-
- fn parse_block<'t>(
- &mut self,
- selector: Self::Prelude,
- start: &ParserState,
- input: &mut Parser<'i, 't>,
- ) -> Result<Self::QualifiedRule, ParseError<'i>> {
- let mut block = PropertyDeclarationBlock::new();
- let declarations = &mut self.declarations;
- self.context
- .nest_for_rule(CssRuleType::Keyframe, |context| {
- let mut parser = KeyframeDeclarationParser {
- context: &context,
- declarations,
- };
- let mut iter = RuleBodyParser::new(input, &mut parser);
- while let Some(declaration) = iter.next() {
- match declaration {
- Ok(()) => {
- block.extend(iter.parser.declarations.drain(), Importance::Normal);
- },
- Err((error, slice)) => {
- iter.parser.declarations.clear();
- let location = error.location;
- let error =
- ContextualParseError::UnsupportedKeyframePropertyDeclaration(
- slice, error,
- );
- context.log_css_error(location, error);
- },
- }
- // `parse_important` is not called here, `!important` is not allowed in keyframe blocks.
- }
- });
- Ok(Arc::new(self.shared_lock.wrap(Keyframe {
- selector,
- block: Arc::new(self.shared_lock.wrap(block)),
- source_location: start.source_location(),
- })))
- }
-}
-
-impl<'a, 'b, 'i> RuleBodyItemParser<'i, Arc<Locked<Keyframe>>, StyleParseErrorKind<'i>>
- for KeyframeListParser<'a, 'b>
-{
- fn parse_qualified(&self) -> bool {
- true
- }
- fn parse_declarations(&self) -> bool {
- false
- }
-}
-
-struct KeyframeDeclarationParser<'a, 'b: 'a> {
- context: &'a ParserContext<'b>,
- declarations: &'a mut SourcePropertyDeclaration,
-}
-
-/// Default methods reject all at rules.
-impl<'a, 'b, 'i> AtRuleParser<'i> for KeyframeDeclarationParser<'a, 'b> {
- type Prelude = ();
- type AtRule = ();
- type Error = StyleParseErrorKind<'i>;
-}
-
-impl<'a, 'b, 'i> QualifiedRuleParser<'i> for KeyframeDeclarationParser<'a, 'b> {
- type Prelude = ();
- type QualifiedRule = ();
- type Error = StyleParseErrorKind<'i>;
-}
-
-impl<'a, 'b, 'i> DeclarationParser<'i> for KeyframeDeclarationParser<'a, 'b> {
- type Declaration = ();
- type Error = StyleParseErrorKind<'i>;
-
- fn parse_value<'t>(
- &mut self,
- name: CowRcStr<'i>,
- input: &mut Parser<'i, 't>,
- ) -> Result<(), ParseError<'i>> {
- let id = match PropertyId::parse(&name, self.context) {
- Ok(id) => id,
- Err(()) => {
- return Err(input.new_custom_error(StyleParseErrorKind::UnknownProperty(name)));
- },
- };
-
- // TODO(emilio): Shouldn't this use parse_entirely?
- PropertyDeclaration::parse_into(self.declarations, id, self.context, input)?;
-
- // In case there is still unparsed text in the declaration, we should
- // roll back.
- input.expect_exhausted()?;
-
- Ok(())
- }
-}
-
-impl<'a, 'b, 'i> RuleBodyItemParser<'i, (), StyleParseErrorKind<'i>>
- for KeyframeDeclarationParser<'a, 'b>
-{
- fn parse_qualified(&self) -> bool {
- false
- }
- fn parse_declarations(&self) -> bool {
- true
- }
-}
diff --git a/components/style/stylesheets/layer_rule.rs b/components/style/stylesheets/layer_rule.rs
deleted file mode 100644
index 3ebe6bb34f8..00000000000
--- a/components/style/stylesheets/layer_rule.rs
+++ /dev/null
@@ -1,228 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
-
-//! A [`@layer`][layer] rule.
-//!
-//! [layer]: https://drafts.csswg.org/css-cascade-5/#layering
-
-use crate::parser::{Parse, ParserContext};
-use crate::shared_lock::{DeepCloneParams, DeepCloneWithLock, Locked};
-use crate::shared_lock::{SharedRwLock, SharedRwLockReadGuard, ToCssWithGuard};
-use crate::values::AtomIdent;
-
-use super::CssRules;
-
-use cssparser::{Parser, SourceLocation, Token};
-use servo_arc::Arc;
-use smallvec::SmallVec;
-use std::fmt::{self, Write};
-use style_traits::{CssWriter, ParseError, ToCss};
-
-/// The order of a given layer. We use 16 bits so that we can pack LayerOrder
-/// and CascadeLevel in a single 32-bit struct. If we need more bits we can go
-/// back to packing CascadeLevel in a single byte as we did before.
-#[derive(Clone, Copy, Debug, Eq, Hash, MallocSizeOf, PartialEq, PartialOrd, Ord)]
-pub struct LayerOrder(u16);
-
-impl LayerOrder {
- /// The order of the root layer.
- pub const fn root() -> Self {
- Self(std::u16::MAX - 1)
- }
-
- /// The order of the style attribute layer.
- pub const fn style_attribute() -> Self {
- Self(std::u16::MAX)
- }
-
- /// Returns whether this layer is for the style attribute, which behaves
- /// differently in terms of !important, see
- /// https://github.com/w3c/csswg-drafts/issues/6872
- ///
- /// (This is a bit silly, mind-you, but it's needed so that revert-layer
- /// behaves correctly).
- #[inline]
- pub fn is_style_attribute_layer(&self) -> bool {
- *self == Self::style_attribute()
- }
-
- /// The first cascade layer order.
- pub const fn first() -> Self {
- Self(0)
- }
-
- /// Increment the cascade layer order.
- #[inline]
- pub fn inc(&mut self) {
- if self.0 != std::u16::MAX - 1 {
- self.0 += 1;
- }
- }
-}
-
-/// A `<layer-name>`: https://drafts.csswg.org/css-cascade-5/#typedef-layer-name
-#[derive(Clone, Debug, Eq, Hash, MallocSizeOf, PartialEq, ToShmem)]
-pub struct LayerName(pub SmallVec<[AtomIdent; 1]>);
-
-impl LayerName {
- /// Returns an empty layer name (which isn't a valid final state, so caller
- /// is responsible to fill up the name before use).
- pub fn new_empty() -> Self {
- Self(Default::default())
- }
-
- /// Returns a synthesized name for an anonymous layer.
- pub fn new_anonymous() -> Self {
- use std::sync::atomic::{AtomicUsize, Ordering};
- static NEXT_ANONYMOUS_LAYER_NAME: AtomicUsize = AtomicUsize::new(0);
-
- let mut name = SmallVec::new();
- let next_id = NEXT_ANONYMOUS_LAYER_NAME.fetch_add(1, Ordering::Relaxed);
- // The parens don't _technically_ prevent conflicts with authors, as
- // authors could write escaped parens as part of the identifier, I
- // think, but highly reduces the possibility.
- name.push(AtomIdent::from(&*format!("-moz-anon-layer({})", next_id)));
-
- LayerName(name)
- }
-
- /// Returns the names of the layers. That is, for a layer like `foo.bar`,
- /// it'd return [foo, bar].
- pub fn layer_names(&self) -> &[AtomIdent] {
- &self.0
- }
-}
-
-impl Parse for LayerName {
- fn parse<'i, 't>(
- _: &ParserContext,
- input: &mut Parser<'i, 't>,
- ) -> Result<Self, ParseError<'i>> {
- let mut result = SmallVec::new();
- result.push(AtomIdent::from(&**input.expect_ident()?));
- loop {
- let next_name = input.try_parse(|input| -> Result<AtomIdent, ParseError<'i>> {
- match input.next_including_whitespace()? {
- Token::Delim('.') => {},
- other => {
- let t = other.clone();
- return Err(input.new_unexpected_token_error(t));
- },
- }
-
- let name = match input.next_including_whitespace()? {
- Token::Ident(ref ident) => ident,
- other => {
- let t = other.clone();
- return Err(input.new_unexpected_token_error(t));
- },
- };
-
- Ok(AtomIdent::from(&**name))
- });
-
- match next_name {
- Ok(name) => result.push(name),
- Err(..) => break,
- }
- }
- Ok(LayerName(result))
- }
-}
-
-impl ToCss for LayerName {
- fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
- where
- W: Write,
- {
- let mut first = true;
- for name in self.0.iter() {
- if !first {
- dest.write_char('.')?;
- }
- first = false;
- name.to_css(dest)?;
- }
- Ok(())
- }
-}
-
-#[derive(Debug, ToShmem)]
-/// A block `@layer <name>? { ... }`
-/// https://drafts.csswg.org/css-cascade-5/#layer-block
-pub struct LayerBlockRule {
- /// The layer name, or `None` if anonymous.
- pub name: Option<LayerName>,
- /// The nested rules.
- pub rules: Arc<Locked<CssRules>>,
- /// The source position where this rule was found.
- pub source_location: SourceLocation,
-}
-
-impl ToCssWithGuard for LayerBlockRule {
- fn to_css(
- &self,
- guard: &SharedRwLockReadGuard,
- dest: &mut crate::str::CssStringWriter,
- ) -> fmt::Result {
- dest.write_str("@layer")?;
- if let Some(ref name) = self.name {
- dest.write_char(' ')?;
- name.to_css(&mut CssWriter::new(dest))?;
- }
- self.rules.read_with(guard).to_css_block(guard, dest)
- }
-}
-
-impl DeepCloneWithLock for LayerBlockRule {
- fn deep_clone_with_lock(
- &self,
- lock: &SharedRwLock,
- guard: &SharedRwLockReadGuard,
- params: &DeepCloneParams,
- ) -> Self {
- Self {
- name: self.name.clone(),
- rules: Arc::new(
- lock.wrap(
- self.rules
- .read_with(guard)
- .deep_clone_with_lock(lock, guard, params),
- ),
- ),
- source_location: self.source_location.clone(),
- }
- }
-}
-
-/// A statement `@layer <name>, <name>, <name>;`
-///
-/// https://drafts.csswg.org/css-cascade-5/#layer-empty
-#[derive(Clone, Debug, ToShmem)]
-pub struct LayerStatementRule {
- /// The list of layers to sort.
- pub names: Vec<LayerName>,
- /// The source position where this rule was found.
- pub source_location: SourceLocation,
-}
-
-impl ToCssWithGuard for LayerStatementRule {
- fn to_css(
- &self,
- _: &SharedRwLockReadGuard,
- dest: &mut crate::str::CssStringWriter,
- ) -> fmt::Result {
- let mut writer = CssWriter::new(dest);
- writer.write_str("@layer ")?;
- let mut first = true;
- for name in &*self.names {
- if !first {
- writer.write_str(", ")?;
- }
- first = false;
- name.to_css(&mut writer)?;
- }
- writer.write_char(';')
- }
-}
diff --git a/components/style/stylesheets/loader.rs b/components/style/stylesheets/loader.rs
deleted file mode 100644
index f987cf95972..00000000000
--- a/components/style/stylesheets/loader.rs
+++ /dev/null
@@ -1,31 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
-
-//! The stylesheet loader is the abstraction used to trigger network requests
-//! for `@import` rules.
-
-use crate::media_queries::MediaList;
-use crate::parser::ParserContext;
-use crate::shared_lock::{Locked, SharedRwLock};
-use crate::stylesheets::import_rule::{ImportLayer, ImportRule, ImportSupportsCondition};
-use crate::values::CssUrl;
-use cssparser::SourceLocation;
-use servo_arc::Arc;
-
-/// The stylesheet loader is the abstraction used to trigger network requests
-/// for `@import` rules.
-pub trait StylesheetLoader {
- /// Request a stylesheet after parsing a given `@import` rule, and return
- /// the constructed `@import` rule.
- fn request_stylesheet(
- &self,
- url: CssUrl,
- location: SourceLocation,
- context: &ParserContext,
- lock: &SharedRwLock,
- media: Arc<Locked<MediaList>>,
- supports: Option<ImportSupportsCondition>,
- layer: ImportLayer,
- ) -> Arc<Locked<ImportRule>>;
-}
diff --git a/components/style/stylesheets/media_rule.rs b/components/style/stylesheets/media_rule.rs
deleted file mode 100644
index cde60a16bf7..00000000000
--- a/components/style/stylesheets/media_rule.rs
+++ /dev/null
@@ -1,71 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
-
-//! An [`@media`][media] rule.
-//!
-//! [media]: https://drafts.csswg.org/css-conditional/#at-ruledef-media
-
-use crate::media_queries::MediaList;
-use crate::shared_lock::{DeepCloneParams, DeepCloneWithLock, Locked};
-use crate::shared_lock::{SharedRwLock, SharedRwLockReadGuard, ToCssWithGuard};
-use crate::str::CssStringWriter;
-use crate::stylesheets::CssRules;
-use cssparser::SourceLocation;
-#[cfg(feature = "gecko")]
-use malloc_size_of::{MallocSizeOfOps, MallocUnconditionalShallowSizeOf};
-use servo_arc::Arc;
-use std::fmt::{self, Write};
-use style_traits::{CssWriter, ToCss};
-
-/// An [`@media`][media] rule.
-///
-/// [media]: https://drafts.csswg.org/css-conditional/#at-ruledef-media
-#[derive(Debug, ToShmem)]
-pub struct MediaRule {
- /// The list of media queries used by this media rule.
- pub media_queries: Arc<Locked<MediaList>>,
- /// The nested rules to this media rule.
- pub rules: Arc<Locked<CssRules>>,
- /// The source position where this media rule was found.
- pub source_location: SourceLocation,
-}
-
-impl MediaRule {
- /// Measure heap usage.
- #[cfg(feature = "gecko")]
- pub fn size_of(&self, guard: &SharedRwLockReadGuard, ops: &mut MallocSizeOfOps) -> usize {
- // Measurement of other fields may be added later.
- self.rules.unconditional_shallow_size_of(ops) +
- self.rules.read_with(guard).size_of(guard, ops)
- }
-}
-
-impl ToCssWithGuard for MediaRule {
- // Serialization of MediaRule is not specced.
- // https://drafts.csswg.org/cssom/#serialize-a-css-rule CSSMediaRule
- fn to_css(&self, guard: &SharedRwLockReadGuard, dest: &mut CssStringWriter) -> fmt::Result {
- dest.write_str("@media ")?;
- self.media_queries
- .read_with(guard)
- .to_css(&mut CssWriter::new(dest))?;
- self.rules.read_with(guard).to_css_block(guard, dest)
- }
-}
-
-impl DeepCloneWithLock for MediaRule {
- fn deep_clone_with_lock(
- &self,
- lock: &SharedRwLock,
- guard: &SharedRwLockReadGuard,
- params: &DeepCloneParams,
- ) -> Self {
- let media_queries = self.media_queries.read_with(guard);
- let rules = self.rules.read_with(guard);
- MediaRule {
- media_queries: Arc::new(lock.wrap(media_queries.clone())),
- rules: Arc::new(lock.wrap(rules.deep_clone_with_lock(lock, guard, params))),
- source_location: self.source_location.clone(),
- }
- }
-}
diff --git a/components/style/stylesheets/mod.rs b/components/style/stylesheets/mod.rs
deleted file mode 100644
index 85d6619d135..00000000000
--- a/components/style/stylesheets/mod.rs
+++ /dev/null
@@ -1,582 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
-
-//! Style sheets and their CSS rules.
-
-pub mod container_rule;
-mod counter_style_rule;
-mod document_rule;
-mod font_face_rule;
-pub mod font_feature_values_rule;
-pub mod font_palette_values_rule;
-pub mod import_rule;
-pub mod keyframes_rule;
-pub mod layer_rule;
-mod loader;
-mod media_rule;
-mod namespace_rule;
-pub mod origin;
-mod page_rule;
-mod property_rule;
-mod rule_list;
-mod rule_parser;
-mod rules_iterator;
-mod style_rule;
-mod stylesheet;
-pub mod supports_rule;
-
-#[cfg(feature = "gecko")]
-use crate::gecko_bindings::sugar::refptr::RefCounted;
-#[cfg(feature = "gecko")]
-use crate::gecko_bindings::{bindings, structs};
-use crate::parser::ParserContext;
-use crate::shared_lock::{DeepCloneParams, DeepCloneWithLock, Locked};
-use crate::shared_lock::{SharedRwLock, SharedRwLockReadGuard, ToCssWithGuard};
-use crate::str::CssStringWriter;
-use cssparser::{parse_one_rule, Parser, ParserInput};
-#[cfg(feature = "gecko")]
-use malloc_size_of::{MallocSizeOfOps, MallocUnconditionalShallowSizeOf};
-use servo_arc::Arc;
-use std::borrow::Cow;
-use std::fmt;
-#[cfg(feature = "gecko")]
-use std::mem::{self, ManuallyDrop};
-use style_traits::ParsingMode;
-use to_shmem::{self, SharedMemoryBuilder, ToShmem};
-
-pub use self::container_rule::ContainerRule;
-pub use self::counter_style_rule::CounterStyleRule;
-pub use self::document_rule::DocumentRule;
-pub use self::font_face_rule::FontFaceRule;
-pub use self::font_feature_values_rule::FontFeatureValuesRule;
-pub use self::font_palette_values_rule::FontPaletteValuesRule;
-pub use self::import_rule::ImportRule;
-pub use self::keyframes_rule::KeyframesRule;
-pub use self::layer_rule::{LayerBlockRule, LayerStatementRule};
-pub use self::loader::StylesheetLoader;
-pub use self::media_rule::MediaRule;
-pub use self::namespace_rule::NamespaceRule;
-pub use self::origin::{Origin, OriginSet, OriginSetIterator, PerOrigin, PerOriginIter};
-pub use self::page_rule::{PageRule, PageSelector, PageSelectors};
-pub use self::property_rule::PropertyRule;
-pub use self::rule_list::{CssRules, CssRulesHelpers};
-pub use self::rule_parser::{InsertRuleContext, State, TopLevelRuleParser};
-pub use self::rules_iterator::{AllRules, EffectiveRules};
-pub use self::rules_iterator::{
- EffectiveRulesIterator, NestedRuleIterationCondition, RulesIterator,
-};
-pub use self::style_rule::StyleRule;
-pub use self::stylesheet::{AllowImportRules, SanitizationData, SanitizationKind};
-pub use self::stylesheet::{DocumentStyleSheet, Namespaces, Stylesheet};
-pub use self::stylesheet::{StylesheetContents, StylesheetInDocument, UserAgentStylesheets};
-pub use self::supports_rule::SupportsRule;
-
-/// The CORS mode used for a CSS load.
-#[repr(u8)]
-#[derive(Clone, Copy, Debug, Eq, PartialEq, ToShmem)]
-pub enum CorsMode {
- /// No CORS mode, so cross-origin loads can be done.
- None,
- /// Anonymous CORS request.
- Anonymous,
-}
-
-/// Extra data that the backend may need to resolve url values.
-///
-/// If the usize's lowest bit is 0, then this is a strong reference to a
-/// structs::URLExtraData object.
-///
-/// Otherwise, shifting the usize's bits the right by one gives the
-/// UserAgentStyleSheetID value corresponding to the style sheet whose
-/// URLExtraData this is, which is stored in URLExtraData_sShared. We don't
-/// hold a strong reference to that object from here, but we rely on that
-/// array's objects being held alive until shutdown.
-///
-/// We use this packed representation rather than an enum so that
-/// `from_ptr_ref` can work.
-#[cfg(feature = "gecko")]
-#[derive(PartialEq)]
-#[repr(C)]
-pub struct UrlExtraData(usize);
-
-/// Extra data that the backend may need to resolve url values.
-#[cfg(feature = "servo")]
-#[derive(Clone, Debug, Eq, PartialEq)]
-pub struct UrlExtraData(pub Arc<::url::Url>);
-
-#[cfg(feature = "servo")]
-impl UrlExtraData {
- /// True if this URL scheme is chrome.
- pub fn chrome_rules_enabled(&self) -> bool {
- self.0.scheme() == "chrome"
- }
-
- /// Get the interior Url as a string.
- pub fn as_str(&self) -> &str {
- self.0.as_str()
- }
-}
-
-#[cfg(feature = "servo")]
-impl From<::url::Url> for UrlExtraData {
- fn from(url: ::url::Url) -> Self {
- Self(Arc::new(url))
- }
-}
-
-#[cfg(not(feature = "gecko"))]
-impl ToShmem for UrlExtraData {
- fn to_shmem(&self, _builder: &mut SharedMemoryBuilder) -> to_shmem::Result<Self> {
- unimplemented!("If servo wants to share stylesheets across processes, ToShmem for Url must be implemented");
- }
-}
-
-#[cfg(feature = "gecko")]
-impl Clone for UrlExtraData {
- fn clone(&self) -> UrlExtraData {
- UrlExtraData::new(self.ptr())
- }
-}
-
-#[cfg(feature = "gecko")]
-impl Drop for UrlExtraData {
- fn drop(&mut self) {
- // No need to release when we have an index into URLExtraData_sShared.
- if self.0 & 1 == 0 {
- unsafe {
- self.as_ref().release();
- }
- }
- }
-}
-
-#[cfg(feature = "gecko")]
-impl ToShmem for UrlExtraData {
- fn to_shmem(&self, _builder: &mut SharedMemoryBuilder) -> to_shmem::Result<Self> {
- if self.0 & 1 == 0 {
- let shared_extra_datas = unsafe { &structs::URLExtraData_sShared };
- let self_ptr = self.as_ref() as *const _ as *mut _;
- let sheet_id = shared_extra_datas
- .iter()
- .position(|r| r.mRawPtr == self_ptr);
- let sheet_id = match sheet_id {
- Some(id) => id,
- None => {
- return Err(String::from(
- "ToShmem failed for UrlExtraData: expected sheet's URLExtraData to be in \
- URLExtraData::sShared",
- ));
- },
- };
- Ok(ManuallyDrop::new(UrlExtraData((sheet_id << 1) | 1)))
- } else {
- Ok(ManuallyDrop::new(UrlExtraData(self.0)))
- }
- }
-}
-
-#[cfg(feature = "gecko")]
-impl UrlExtraData {
- /// Create a new UrlExtraData wrapping a pointer to the specified Gecko
- /// URLExtraData object.
- pub fn new(ptr: *mut structs::URLExtraData) -> UrlExtraData {
- unsafe {
- (*ptr).addref();
- }
- UrlExtraData(ptr as usize)
- }
-
- /// True if this URL scheme is chrome.
- #[inline]
- pub fn chrome_rules_enabled(&self) -> bool {
- self.as_ref().mChromeRulesEnabled
- }
-
- /// Create a reference to this `UrlExtraData` from a reference to pointer.
- ///
- /// The pointer must be valid and non null.
- ///
- /// This method doesn't touch refcount.
- #[inline]
- pub unsafe fn from_ptr_ref(ptr: &*mut structs::URLExtraData) -> &Self {
- mem::transmute(ptr)
- }
-
- /// Returns a pointer to the Gecko URLExtraData object.
- pub fn ptr(&self) -> *mut structs::URLExtraData {
- if self.0 & 1 == 0 {
- self.0 as *mut structs::URLExtraData
- } else {
- unsafe {
- let sheet_id = self.0 >> 1;
- structs::URLExtraData_sShared[sheet_id].mRawPtr
- }
- }
- }
-
- fn as_ref(&self) -> &structs::URLExtraData {
- unsafe { &*(self.ptr() as *const structs::URLExtraData) }
- }
-}
-
-#[cfg(feature = "gecko")]
-impl fmt::Debug for UrlExtraData {
- fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
- macro_rules! define_debug_struct {
- ($struct_name:ident, $gecko_class:ident, $debug_fn:ident) => {
- struct $struct_name(*mut structs::$gecko_class);
- impl fmt::Debug for $struct_name {
- fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
- use nsstring::nsCString;
- let mut spec = nsCString::new();
- unsafe {
- bindings::$debug_fn(self.0, &mut spec);
- }
- spec.fmt(formatter)
- }
- }
- };
- }
-
- define_debug_struct!(DebugURI, nsIURI, Gecko_nsIURI_Debug);
- define_debug_struct!(
- DebugReferrerInfo,
- nsIReferrerInfo,
- Gecko_nsIReferrerInfo_Debug
- );
-
- formatter
- .debug_struct("URLExtraData")
- .field("chrome_rules_enabled", &self.chrome_rules_enabled())
- .field("base", &DebugURI(self.as_ref().mBaseURI.raw()))
- .field(
- "referrer",
- &DebugReferrerInfo(self.as_ref().mReferrerInfo.raw()),
- )
- .finish()
- }
-}
-
-// XXX We probably need to figure out whether we should mark Eq here.
-// It is currently marked so because properties::UnparsedValue wants Eq.
-#[cfg(feature = "gecko")]
-impl Eq for UrlExtraData {}
-
-/// A CSS rule.
-///
-/// TODO(emilio): Lots of spec links should be around.
-#[derive(Clone, Debug, ToShmem)]
-#[allow(missing_docs)]
-pub enum CssRule {
- // No Charset here, CSSCharsetRule has been removed from CSSOM
- // https://drafts.csswg.org/cssom/#changes-from-5-december-2013
- Namespace(Arc<NamespaceRule>),
- Import(Arc<Locked<ImportRule>>),
- Style(Arc<Locked<StyleRule>>),
- Media(Arc<MediaRule>),
- Container(Arc<ContainerRule>),
- FontFace(Arc<Locked<FontFaceRule>>),
- FontFeatureValues(Arc<FontFeatureValuesRule>),
- FontPaletteValues(Arc<FontPaletteValuesRule>),
- CounterStyle(Arc<Locked<CounterStyleRule>>),
- Keyframes(Arc<Locked<KeyframesRule>>),
- Supports(Arc<SupportsRule>),
- Page(Arc<Locked<PageRule>>),
- Property(Arc<PropertyRule>),
- Document(Arc<DocumentRule>),
- LayerBlock(Arc<LayerBlockRule>),
- LayerStatement(Arc<LayerStatementRule>),
-}
-
-impl CssRule {
- /// Measure heap usage.
- #[cfg(feature = "gecko")]
- fn size_of(&self, guard: &SharedRwLockReadGuard, ops: &mut MallocSizeOfOps) -> usize {
- match *self {
- // Not all fields are currently fully measured. Extra measurement
- // may be added later.
- CssRule::Namespace(_) => 0,
-
- // We don't need to measure ImportRule::stylesheet because we measure
- // it on the C++ side in the child list of the ServoStyleSheet.
- CssRule::Import(_) => 0,
-
- CssRule::Style(ref lock) => {
- lock.unconditional_shallow_size_of(ops) + lock.read_with(guard).size_of(guard, ops)
- },
- CssRule::Media(ref arc) => {
- arc.unconditional_shallow_size_of(ops) + arc.size_of(guard, ops)
- },
- CssRule::Container(ref arc) => {
- arc.unconditional_shallow_size_of(ops) + arc.size_of(guard, ops)
- },
- CssRule::FontFace(_) => 0,
- CssRule::FontFeatureValues(_) => 0,
- CssRule::FontPaletteValues(_) => 0,
- CssRule::CounterStyle(_) => 0,
- CssRule::Keyframes(_) => 0,
- CssRule::Supports(ref arc) => {
- arc.unconditional_shallow_size_of(ops) + arc.size_of(guard, ops)
- },
- CssRule::Page(ref lock) => {
- lock.unconditional_shallow_size_of(ops) + lock.read_with(guard).size_of(guard, ops)
- },
- CssRule::Property(ref rule) => {
- rule.unconditional_shallow_size_of(ops) + rule.size_of(guard, ops)
- },
- CssRule::Document(ref arc) => {
- arc.unconditional_shallow_size_of(ops) + arc.size_of(guard, ops)
- },
- // TODO(emilio): Add memory reporting for these rules.
- CssRule::LayerBlock(_) | CssRule::LayerStatement(_) => 0,
- }
- }
-}
-
-/// https://drafts.csswg.org/cssom-1/#dom-cssrule-type
-#[allow(missing_docs)]
-#[derive(Clone, Copy, Debug, Eq, FromPrimitive, PartialEq)]
-#[repr(u8)]
-pub enum CssRuleType {
- // https://drafts.csswg.org/cssom/#the-cssrule-interface
- Style = 1,
- // Charset = 2, // Historical
- Import = 3,
- Media = 4,
- FontFace = 5,
- Page = 6,
- // https://drafts.csswg.org/css-animations-1/#interface-cssrule-idl
- Keyframes = 7,
- Keyframe = 8,
- // https://drafts.csswg.org/cssom/#the-cssrule-interface
- // Margin = 9, // Not implemented yet.
- Namespace = 10,
- // https://drafts.csswg.org/css-counter-styles-3/#extentions-to-cssrule-interface
- CounterStyle = 11,
- // https://drafts.csswg.org/css-conditional-3/#extentions-to-cssrule-interface
- Supports = 12,
- // https://www.w3.org/TR/2012/WD-css3-conditional-20120911/#extentions-to-cssrule-interface
- Document = 13,
- // https://drafts.csswg.org/css-fonts/#om-fontfeaturevalues
- FontFeatureValues = 14,
- // After viewport, all rules should return 0 from the API, but we still need
- // a constant somewhere.
- LayerBlock = 16,
- LayerStatement = 17,
- Container = 18,
- FontPaletteValues = 19,
- // 20 is an arbitrary number to use for Property.
- Property = 20,
-}
-
-impl CssRuleType {
- /// Returns a bit that identifies this rule type.
- #[inline]
- pub const fn bit(self) -> u32 {
- 1 << self as u32
- }
-}
-
-/// Set of rule types.
-#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
-pub struct CssRuleTypes(u32);
-
-impl From<CssRuleType> for CssRuleTypes {
- fn from(ty: CssRuleType) -> Self {
- Self(ty.bit())
- }
-}
-
-impl CssRuleTypes {
- /// Returns whether the rule is in the current set.
- #[inline]
- pub fn contains(self, ty: CssRuleType) -> bool {
- self.0 & ty.bit() != 0
- }
-
- /// Returns all the rules specified in the set.
- pub fn bits(self) -> u32 {
- self.0
- }
-
- /// Inserts a rule type into the set.
- #[inline]
- pub fn insert(&mut self, ty: CssRuleType) {
- self.0 |= ty.bit()
- }
-}
-
-#[allow(missing_docs)]
-pub enum RulesMutateError {
- Syntax,
- IndexSize,
- HierarchyRequest,
- InvalidState,
-}
-
-impl CssRule {
- /// Returns the CSSOM rule type of this rule.
- pub fn rule_type(&self) -> CssRuleType {
- match *self {
- CssRule::Style(_) => CssRuleType::Style,
- CssRule::Import(_) => CssRuleType::Import,
- CssRule::Media(_) => CssRuleType::Media,
- CssRule::FontFace(_) => CssRuleType::FontFace,
- CssRule::FontFeatureValues(_) => CssRuleType::FontFeatureValues,
- CssRule::FontPaletteValues(_) => CssRuleType::FontPaletteValues,
- CssRule::CounterStyle(_) => CssRuleType::CounterStyle,
- CssRule::Keyframes(_) => CssRuleType::Keyframes,
- CssRule::Namespace(_) => CssRuleType::Namespace,
- CssRule::Supports(_) => CssRuleType::Supports,
- CssRule::Page(_) => CssRuleType::Page,
- CssRule::Property(_) => CssRuleType::Property,
- CssRule::Document(_) => CssRuleType::Document,
- CssRule::LayerBlock(_) => CssRuleType::LayerBlock,
- CssRule::LayerStatement(_) => CssRuleType::LayerStatement,
- CssRule::Container(_) => CssRuleType::Container,
- }
- }
-
- /// Parse a CSS rule.
- ///
- /// Returns a parsed CSS rule and the final state of the parser.
- ///
- /// Input state is None for a nested rule
- pub fn parse(
- css: &str,
- insert_rule_context: InsertRuleContext,
- parent_stylesheet_contents: &StylesheetContents,
- shared_lock: &SharedRwLock,
- state: State,
- loader: Option<&dyn StylesheetLoader>,
- allow_import_rules: AllowImportRules,
- ) -> Result<Self, RulesMutateError> {
- let url_data = parent_stylesheet_contents.url_data.read();
- let namespaces = parent_stylesheet_contents.namespaces.read();
- let context = ParserContext::new(
- parent_stylesheet_contents.origin,
- &url_data,
- None,
- ParsingMode::DEFAULT,
- parent_stylesheet_contents.quirks_mode,
- Cow::Borrowed(&*namespaces),
- None,
- None,
- );
-
- let mut input = ParserInput::new(css);
- let mut input = Parser::new(&mut input);
-
- // nested rules are in the body state
- let mut rule_parser = TopLevelRuleParser {
- context,
- shared_lock: &shared_lock,
- loader,
- state,
- dom_error: None,
- insert_rule_context: Some(insert_rule_context),
- allow_import_rules,
- declaration_parser_state: Default::default(),
- rules: Default::default(),
- };
-
- match parse_one_rule(&mut input, &mut rule_parser) {
- Ok(_) => Ok(rule_parser.rules.pop().unwrap()),
- Err(_) => Err(rule_parser.dom_error.unwrap_or(RulesMutateError::Syntax)),
- }
- }
-}
-
-impl DeepCloneWithLock for CssRule {
- /// Deep clones this CssRule.
- fn deep_clone_with_lock(
- &self,
- lock: &SharedRwLock,
- guard: &SharedRwLockReadGuard,
- params: &DeepCloneParams,
- ) -> CssRule {
- match *self {
- CssRule::Namespace(ref arc) => CssRule::Namespace(arc.clone()),
- CssRule::Import(ref arc) => {
- let rule = arc
- .read_with(guard)
- .deep_clone_with_lock(lock, guard, params);
- CssRule::Import(Arc::new(lock.wrap(rule)))
- },
- CssRule::Style(ref arc) => {
- let rule = arc.read_with(guard);
- CssRule::Style(Arc::new(
- lock.wrap(rule.deep_clone_with_lock(lock, guard, params)),
- ))
- },
- CssRule::Container(ref arc) => {
- CssRule::Container(Arc::new(arc.deep_clone_with_lock(lock, guard, params)))
- },
- CssRule::Media(ref arc) => {
- CssRule::Media(Arc::new(arc.deep_clone_with_lock(lock, guard, params)))
- },
- CssRule::FontFace(ref arc) => {
- let rule = arc.read_with(guard);
- CssRule::FontFace(Arc::new(lock.wrap(rule.clone())))
- },
- CssRule::FontFeatureValues(ref arc) => CssRule::FontFeatureValues(arc.clone()),
- CssRule::FontPaletteValues(ref arc) => CssRule::FontPaletteValues(arc.clone()),
- CssRule::CounterStyle(ref arc) => {
- let rule = arc.read_with(guard);
- CssRule::CounterStyle(Arc::new(lock.wrap(rule.clone())))
- },
- CssRule::Keyframes(ref arc) => {
- let rule = arc.read_with(guard);
- CssRule::Keyframes(Arc::new(
- lock.wrap(rule.deep_clone_with_lock(lock, guard, params)),
- ))
- },
- CssRule::Supports(ref arc) => {
- CssRule::Supports(Arc::new(arc.deep_clone_with_lock(lock, guard, params)))
- },
- CssRule::Page(ref arc) => {
- let rule = arc.read_with(guard);
- CssRule::Page(Arc::new(
- lock.wrap(rule.deep_clone_with_lock(lock, guard, params)),
- ))
- },
- CssRule::Property(ref arc) => {
- // @property rules are immutable, so we don't need any of the `Locked`
- // shenanigans, actually, and can just share the rule.
- CssRule::Property(arc.clone())
- },
- CssRule::Document(ref arc) => {
- CssRule::Document(Arc::new(arc.deep_clone_with_lock(lock, guard, params)))
- },
- CssRule::LayerStatement(ref arc) => CssRule::LayerStatement(arc.clone()),
- CssRule::LayerBlock(ref arc) => {
- CssRule::LayerBlock(Arc::new(arc.deep_clone_with_lock(lock, guard, params)))
- },
- }
- }
-}
-
-impl ToCssWithGuard for CssRule {
- // https://drafts.csswg.org/cssom/#serialize-a-css-rule
- fn to_css(&self, guard: &SharedRwLockReadGuard, dest: &mut CssStringWriter) -> fmt::Result {
- match *self {
- CssRule::Namespace(ref rule) => rule.to_css(guard, dest),
- CssRule::Import(ref lock) => lock.read_with(guard).to_css(guard, dest),
- CssRule::Style(ref lock) => lock.read_with(guard).to_css(guard, dest),
- CssRule::FontFace(ref lock) => lock.read_with(guard).to_css(guard, dest),
- CssRule::FontFeatureValues(ref rule) => rule.to_css(guard, dest),
- CssRule::FontPaletteValues(ref rule) => rule.to_css(guard, dest),
- CssRule::CounterStyle(ref lock) => lock.read_with(guard).to_css(guard, dest),
- CssRule::Keyframes(ref lock) => lock.read_with(guard).to_css(guard, dest),
- CssRule::Media(ref rule) => rule.to_css(guard, dest),
- CssRule::Supports(ref rule) => rule.to_css(guard, dest),
- CssRule::Page(ref lock) => lock.read_with(guard).to_css(guard, dest),
- CssRule::Property(ref rule) => rule.to_css(guard, dest),
- CssRule::Document(ref rule) => rule.to_css(guard, dest),
- CssRule::LayerBlock(ref rule) => rule.to_css(guard, dest),
- CssRule::LayerStatement(ref rule) => rule.to_css(guard, dest),
- CssRule::Container(ref rule) => rule.to_css(guard, dest),
- }
- }
-}
diff --git a/components/style/stylesheets/namespace_rule.rs b/components/style/stylesheets/namespace_rule.rs
deleted file mode 100644
index ad980b70a8b..00000000000
--- a/components/style/stylesheets/namespace_rule.rs
+++ /dev/null
@@ -1,43 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
-
-//! The `@namespace` at-rule.
-
-use crate::shared_lock::{SharedRwLockReadGuard, ToCssWithGuard};
-use crate::str::CssStringWriter;
-use crate::{Namespace, Prefix};
-use cssparser::SourceLocation;
-use std::fmt::{self, Write};
-use style_traits::{CssWriter, ToCss};
-
-/// A `@namespace` rule.
-#[derive(Clone, Debug, PartialEq, ToShmem)]
-#[allow(missing_docs)]
-pub struct NamespaceRule {
- /// The namespace prefix, and `None` if it's the default Namespace
- pub prefix: Option<Prefix>,
- /// The actual namespace url.
- pub url: Namespace,
- /// The source location this rule was found at.
- pub source_location: SourceLocation,
-}
-
-impl ToCssWithGuard for NamespaceRule {
- // https://drafts.csswg.org/cssom/#serialize-a-css-rule CSSNamespaceRule
- fn to_css(
- &self,
- _guard: &SharedRwLockReadGuard,
- dest_str: &mut CssStringWriter,
- ) -> fmt::Result {
- let mut dest = CssWriter::new(dest_str);
- dest.write_str("@namespace ")?;
- if let Some(ref prefix) = self.prefix {
- prefix.to_css(&mut dest)?;
- dest.write_char(' ')?;
- }
- dest.write_str("url(")?;
- self.url.to_string().to_css(&mut dest)?;
- dest.write_str(");")
- }
-}
diff --git a/components/style/stylesheets/origin.rs b/components/style/stylesheets/origin.rs
deleted file mode 100644
index 27ad3fa184a..00000000000
--- a/components/style/stylesheets/origin.rs
+++ /dev/null
@@ -1,247 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
-
-//! [CSS cascade origins](https://drafts.csswg.org/css-cascade/#cascading-origins).
-
-use std::marker::PhantomData;
-use std::ops::BitOrAssign;
-
-/// Each style rule has an origin, which determines where it enters the cascade.
-///
-/// <https://drafts.csswg.org/css-cascade/#cascading-origins>
-#[derive(Clone, Copy, Debug, Eq, MallocSizeOf, PartialEq, ToShmem, PartialOrd, Ord)]
-#[repr(u8)]
-pub enum Origin {
- /// <https://drafts.csswg.org/css-cascade/#cascade-origin-user-agent>
- UserAgent = 0x1,
-
- /// <https://drafts.csswg.org/css-cascade/#cascade-origin-user>
- User = 0x2,
-
- /// <https://drafts.csswg.org/css-cascade/#cascade-origin-author>
- Author = 0x4,
-}
-
-impl Origin {
- /// Returns an origin that goes in order for `index`.
- ///
- /// This is used for iterating across origins.
- fn from_index(index: i8) -> Option<Self> {
- Some(match index {
- 0 => Origin::Author,
- 1 => Origin::User,
- 2 => Origin::UserAgent,
- _ => return None,
- })
- }
-
- fn to_index(self) -> i8 {
- match self {
- Origin::Author => 0,
- Origin::User => 1,
- Origin::UserAgent => 2,
- }
- }
-
- /// Returns an iterator from this origin, towards all the less specific
- /// origins. So for `UserAgent`, it'd iterate through all origins.
- #[inline]
- pub fn following_including(self) -> OriginSetIterator {
- OriginSetIterator {
- set: OriginSet::ORIGIN_USER | OriginSet::ORIGIN_AUTHOR | OriginSet::ORIGIN_USER_AGENT,
- cur: self.to_index(),
- rev: true,
- }
- }
-}
-
-bitflags! {
- /// A set of origins. This is equivalent to Gecko's OriginFlags.
- #[derive(MallocSizeOf)]
- pub struct OriginSet: u8 {
- /// <https://drafts.csswg.org/css-cascade/#cascade-origin-user-agent>
- const ORIGIN_USER_AGENT = Origin::UserAgent as u8;
- /// <https://drafts.csswg.org/css-cascade/#cascade-origin-user>
- const ORIGIN_USER = Origin::User as u8;
- /// <https://drafts.csswg.org/css-cascade/#cascade-origin-author>
- const ORIGIN_AUTHOR = Origin::Author as u8;
- }
-}
-
-impl OriginSet {
- /// Returns an iterator over the origins present in this `OriginSet`.
- ///
- /// See the `OriginSet` documentation for information about the order
- /// origins are iterated.
- pub fn iter(&self) -> OriginSetIterator {
- OriginSetIterator {
- set: *self,
- cur: 0,
- rev: false,
- }
- }
-}
-
-impl From<Origin> for OriginSet {
- fn from(origin: Origin) -> Self {
- Self::from_bits_truncate(origin as u8)
- }
-}
-
-impl BitOrAssign<Origin> for OriginSet {
- fn bitor_assign(&mut self, origin: Origin) {
- *self |= OriginSet::from(origin);
- }
-}
-
-/// Iterates over the origins present in an `OriginSet`, in order from
-/// highest priority (author) to lower (user agent).
-#[derive(Clone)]
-pub struct OriginSetIterator {
- set: OriginSet,
- cur: i8,
- rev: bool,
-}
-
-impl Iterator for OriginSetIterator {
- type Item = Origin;
-
- fn next(&mut self) -> Option<Origin> {
- loop {
- let origin = Origin::from_index(self.cur)?;
-
- if self.rev {
- self.cur -= 1;
- } else {
- self.cur += 1;
- }
-
- if self.set.contains(origin.into()) {
- return Some(origin);
- }
- }
- }
-}
-
-/// An object that stores a `T` for each origin of the CSS cascade.
-#[derive(Debug, Default, MallocSizeOf)]
-pub struct PerOrigin<T> {
- /// Data for `Origin::UserAgent`.
- pub user_agent: T,
-
- /// Data for `Origin::User`.
- pub user: T,
-
- /// Data for `Origin::Author`.
- pub author: T,
-}
-
-impl<T> PerOrigin<T> {
- /// Returns a reference to the per-origin data for the specified origin.
- #[inline]
- pub fn borrow_for_origin(&self, origin: &Origin) -> &T {
- match *origin {
- Origin::UserAgent => &self.user_agent,
- Origin::User => &self.user,
- Origin::Author => &self.author,
- }
- }
-
- /// Returns a mutable reference to the per-origin data for the specified
- /// origin.
- #[inline]
- pub fn borrow_mut_for_origin(&mut self, origin: &Origin) -> &mut T {
- match *origin {
- Origin::UserAgent => &mut self.user_agent,
- Origin::User => &mut self.user,
- Origin::Author => &mut self.author,
- }
- }
-
- /// Iterates over references to per-origin extra style data, from highest
- /// level (author) to lowest (user agent).
- pub fn iter_origins(&self) -> PerOriginIter<T> {
- PerOriginIter {
- data: &self,
- cur: 0,
- rev: false,
- }
- }
-
- /// Iterates over references to per-origin extra style data, from lowest
- /// level (user agent) to highest (author).
- pub fn iter_origins_rev(&self) -> PerOriginIter<T> {
- PerOriginIter {
- data: &self,
- cur: 2,
- rev: true,
- }
- }
-
- /// Iterates over mutable references to per-origin extra style data, from
- /// highest level (author) to lowest (user agent).
- pub fn iter_mut_origins(&mut self) -> PerOriginIterMut<T> {
- PerOriginIterMut {
- data: self,
- cur: 0,
- _marker: PhantomData,
- }
- }
-}
-
-/// Iterator over `PerOrigin<T>`, from highest level (author) to lowest
-/// (user agent).
-///
-/// We rely on this specific order for correctly looking up @font-face,
-/// @counter-style and @keyframes rules.
-pub struct PerOriginIter<'a, T: 'a> {
- data: &'a PerOrigin<T>,
- cur: i8,
- rev: bool,
-}
-
-impl<'a, T> Iterator for PerOriginIter<'a, T>
-where
- T: 'a,
-{
- type Item = (&'a T, Origin);
-
- fn next(&mut self) -> Option<Self::Item> {
- let origin = Origin::from_index(self.cur)?;
-
- self.cur += if self.rev { -1 } else { 1 };
-
- Some((self.data.borrow_for_origin(&origin), origin))
- }
-}
-
-/// Like `PerOriginIter<T>`, but iterates over mutable references to the
-/// per-origin data.
-///
-/// We must use unsafe code here since it's not possible for the borrow
-/// checker to know that we are safely returning a different reference
-/// each time from `next()`.
-pub struct PerOriginIterMut<'a, T: 'a> {
- data: *mut PerOrigin<T>,
- cur: i8,
- _marker: PhantomData<&'a mut PerOrigin<T>>,
-}
-
-impl<'a, T> Iterator for PerOriginIterMut<'a, T>
-where
- T: 'a,
-{
- type Item = (&'a mut T, Origin);
-
- fn next(&mut self) -> Option<Self::Item> {
- let origin = Origin::from_index(self.cur)?;
-
- self.cur += 1;
-
- Some((
- unsafe { (*self.data).borrow_mut_for_origin(&origin) },
- origin,
- ))
- }
-}
diff --git a/components/style/stylesheets/page_rule.rs b/components/style/stylesheets/page_rule.rs
deleted file mode 100644
index 5cd2458aa25..00000000000
--- a/components/style/stylesheets/page_rule.rs
+++ /dev/null
@@ -1,145 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
-
-//! A [`@page`][page] rule.
-//!
-//! [page]: https://drafts.csswg.org/css2/page.html#page-box
-
-use crate::parser::{Parse, ParserContext};
-use crate::properties::PropertyDeclarationBlock;
-use crate::shared_lock::{DeepCloneParams, DeepCloneWithLock, Locked};
-use crate::shared_lock::{SharedRwLock, SharedRwLockReadGuard, ToCssWithGuard};
-use crate::str::CssStringWriter;
-use crate::values::{AtomIdent, CustomIdent};
-use cssparser::{Parser, SourceLocation};
-#[cfg(feature = "gecko")]
-use malloc_size_of::{MallocSizeOf, MallocSizeOfOps, MallocUnconditionalShallowSizeOf};
-use servo_arc::Arc;
-use std::fmt::{self, Write};
-use style_traits::{CssWriter, ParseError, ToCss};
-
-/// Type of a single [`@page`][page selector]
-///
-/// We do not support pseudo selectors yet.
-/// [page-selectors]: https://drafts.csswg.org/css2/page.html#page-selectors
-#[derive(Clone, Debug, Eq, MallocSizeOf, PartialEq, SpecifiedValueInfo, ToCss, ToShmem)]
-pub struct PageSelector(pub AtomIdent);
-
-impl PageSelector {
- /// Checks if the ident matches a page-name's ident.
- ///
- /// This does not currently take pseudo selectors into account.
- #[inline]
- pub fn ident_matches(&self, other: &CustomIdent) -> bool {
- self.0 .0 == other.0
- }
-}
-
-impl Parse for PageSelector {
- fn parse<'i, 't>(
- _context: &ParserContext,
- input: &mut Parser<'i, 't>,
- ) -> Result<Self, ParseError<'i>> {
- let s = input.expect_ident()?;
- Ok(PageSelector(AtomIdent::from(&**s)))
- }
-}
-
-/// A list of [`@page`][page selectors]
-///
-/// [page-selectors]: https://drafts.csswg.org/css2/page.html#page-selectors
-#[derive(Clone, Debug, Default, MallocSizeOf, ToCss, ToShmem)]
-#[css(comma)]
-pub struct PageSelectors(#[css(iterable)] pub Box<[PageSelector]>);
-
-impl PageSelectors {
- /// Creates a new PageSelectors from a Vec, as from parse_comma_separated
- #[inline]
- pub fn new(s: Vec<PageSelector>) -> Self {
- PageSelectors(s.into())
- }
- /// Returns true iff there are any page selectors
- #[inline]
- pub fn is_empty(&self) -> bool {
- self.as_slice().is_empty()
- }
- /// Get the underlying PageSelector data as a slice
- #[inline]
- pub fn as_slice(&self) -> &[PageSelector] {
- &*self.0
- }
-}
-
-impl Parse for PageSelectors {
- fn parse<'i, 't>(
- context: &ParserContext,
- input: &mut Parser<'i, 't>,
- ) -> Result<Self, ParseError<'i>> {
- Ok(PageSelectors::new(input.parse_comma_separated(|i| {
- PageSelector::parse(context, i)
- })?))
- }
-}
-
-/// A [`@page`][page] rule.
-///
-/// This implements only a limited subset of the CSS
-/// 2.2 syntax.
-///
-/// [page]: https://drafts.csswg.org/css2/page.html#page-box
-/// [page-selectors]: https://drafts.csswg.org/css2/page.html#page-selectors
-#[derive(Clone, Debug, ToShmem)]
-pub struct PageRule {
- /// Selectors of the page-rule
- pub selectors: PageSelectors,
- /// The declaration block this page rule contains.
- pub block: Arc<Locked<PropertyDeclarationBlock>>,
- /// The source position this rule was found at.
- pub source_location: SourceLocation,
-}
-
-impl PageRule {
- /// Measure heap usage.
- #[cfg(feature = "gecko")]
- pub fn size_of(&self, guard: &SharedRwLockReadGuard, ops: &mut MallocSizeOfOps) -> usize {
- // Measurement of other fields may be added later.
- self.block.unconditional_shallow_size_of(ops) +
- self.block.read_with(guard).size_of(ops) +
- self.selectors.size_of(ops)
- }
-}
-
-impl ToCssWithGuard for PageRule {
- /// Serialization of PageRule is not specced, adapted from steps for
- /// StyleRule.
- fn to_css(&self, guard: &SharedRwLockReadGuard, dest: &mut CssStringWriter) -> fmt::Result {
- dest.write_str("@page ")?;
- if !self.selectors.is_empty() {
- self.selectors.to_css(&mut CssWriter::new(dest))?;
- dest.write_char(' ')?;
- }
- dest.write_str("{ ")?;
- let declaration_block = self.block.read_with(guard);
- declaration_block.to_css(dest)?;
- if !declaration_block.declarations().is_empty() {
- dest.write_char(' ')?;
- }
- dest.write_char('}')
- }
-}
-
-impl DeepCloneWithLock for PageRule {
- fn deep_clone_with_lock(
- &self,
- lock: &SharedRwLock,
- guard: &SharedRwLockReadGuard,
- _params: &DeepCloneParams,
- ) -> Self {
- PageRule {
- selectors: self.selectors.clone(),
- block: Arc::new(lock.wrap(self.block.read_with(&guard).clone())),
- source_location: self.source_location.clone(),
- }
- }
-}
diff --git a/components/style/stylesheets/property_rule.rs b/components/style/stylesheets/property_rule.rs
deleted file mode 100644
index 1d1c1c982e9..00000000000
--- a/components/style/stylesheets/property_rule.rs
+++ /dev/null
@@ -1,5 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
-
-pub use crate::properties_and_values::rule::PropertyRuleData as PropertyRule;
diff --git a/components/style/stylesheets/rule_list.rs b/components/style/stylesheets/rule_list.rs
deleted file mode 100644
index ab747565ffe..00000000000
--- a/components/style/stylesheets/rule_list.rs
+++ /dev/null
@@ -1,198 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
-
-//! A list of CSS rules.
-
-use crate::shared_lock::{DeepCloneParams, DeepCloneWithLock, Locked};
-use crate::shared_lock::{SharedRwLock, SharedRwLockReadGuard, ToCssWithGuard};
-use crate::str::CssStringWriter;
-use crate::stylesheets::loader::StylesheetLoader;
-use crate::stylesheets::rule_parser::{InsertRuleContext, State};
-use crate::stylesheets::stylesheet::StylesheetContents;
-use crate::stylesheets::{AllowImportRules, CssRule, RulesMutateError};
-#[cfg(feature = "gecko")]
-use malloc_size_of::{MallocShallowSizeOf, MallocSizeOfOps};
-use servo_arc::Arc;
-use std::fmt::{self, Write};
-
-/// A list of CSS rules.
-#[derive(Debug, ToShmem)]
-pub struct CssRules(pub Vec<CssRule>);
-
-impl CssRules {
- /// Whether this CSS rules is empty.
- pub fn is_empty(&self) -> bool {
- self.0.is_empty()
- }
-}
-
-impl DeepCloneWithLock for CssRules {
- fn deep_clone_with_lock(
- &self,
- lock: &SharedRwLock,
- guard: &SharedRwLockReadGuard,
- params: &DeepCloneParams,
- ) -> Self {
- CssRules(
- self.0
- .iter()
- .map(|x| x.deep_clone_with_lock(lock, guard, params))
- .collect(),
- )
- }
-}
-
-impl CssRules {
- /// Measure heap usage.
- #[cfg(feature = "gecko")]
- pub fn size_of(&self, guard: &SharedRwLockReadGuard, ops: &mut MallocSizeOfOps) -> usize {
- let mut n = self.0.shallow_size_of(ops);
- for rule in self.0.iter() {
- n += rule.size_of(guard, ops);
- }
- n
- }
-
- /// Trivially construct a new set of CSS rules.
- pub fn new(rules: Vec<CssRule>, shared_lock: &SharedRwLock) -> Arc<Locked<CssRules>> {
- Arc::new(shared_lock.wrap(CssRules(rules)))
- }
-
- /// Returns whether all the rules in this list are namespace or import
- /// rules.
- fn only_ns_or_import(&self) -> bool {
- self.0.iter().all(|r| match *r {
- CssRule::Namespace(..) | CssRule::Import(..) => true,
- _ => false,
- })
- }
-
- /// <https://drafts.csswg.org/cssom/#remove-a-css-rule>
- pub fn remove_rule(&mut self, index: usize) -> Result<(), RulesMutateError> {
- // Step 1, 2
- if index >= self.0.len() {
- return Err(RulesMutateError::IndexSize);
- }
-
- {
- // Step 3
- let ref rule = self.0[index];
-
- // Step 4
- if let CssRule::Namespace(..) = *rule {
- if !self.only_ns_or_import() {
- return Err(RulesMutateError::InvalidState);
- }
- }
- }
-
- // Step 5, 6
- self.0.remove(index);
- Ok(())
- }
-
- /// Serializes this CSSRules to CSS text as a block of rules.
- ///
- /// This should be speced into CSSOM spec at some point. See
- /// <https://github.com/w3c/csswg-drafts/issues/1985>
- pub fn to_css_block(
- &self,
- guard: &SharedRwLockReadGuard,
- dest: &mut CssStringWriter,
- ) -> fmt::Result {
- dest.write_str(" {")?;
- self.to_css_block_without_opening(guard, dest)
- }
-
- /// As above, but without the opening curly bracket. That's needed for nesting.
- pub fn to_css_block_without_opening(
- &self,
- guard: &SharedRwLockReadGuard,
- dest: &mut CssStringWriter,
- ) -> fmt::Result {
- for rule in self.0.iter() {
- dest.write_str("\n ")?;
- rule.to_css(guard, dest)?;
- }
- dest.write_str("\n}")
- }
-}
-
-/// A trait to implement helpers for `Arc<Locked<CssRules>>`.
-pub trait CssRulesHelpers {
- /// <https://drafts.csswg.org/cssom/#insert-a-css-rule>
- ///
- /// Written in this funky way because parsing an @import rule may cause us
- /// to clone a stylesheet from the same document due to caching in the CSS
- /// loader.
- ///
- /// TODO(emilio): We could also pass the write guard down into the loader
- /// instead, but that seems overkill.
- fn insert_rule(
- &self,
- lock: &SharedRwLock,
- rule: &str,
- parent_stylesheet_contents: &StylesheetContents,
- index: usize,
- nested: bool,
- loader: Option<&dyn StylesheetLoader>,
- allow_import_rules: AllowImportRules,
- ) -> Result<CssRule, RulesMutateError>;
-}
-
-impl CssRulesHelpers for Locked<CssRules> {
- fn insert_rule(
- &self,
- lock: &SharedRwLock,
- rule: &str,
- parent_stylesheet_contents: &StylesheetContents,
- index: usize,
- nested: bool,
- loader: Option<&dyn StylesheetLoader>,
- allow_import_rules: AllowImportRules,
- ) -> Result<CssRule, RulesMutateError> {
- let new_rule = {
- let read_guard = lock.read();
- let rules = self.read_with(&read_guard);
-
- // Step 1, 2
- if index > rules.0.len() {
- return Err(RulesMutateError::IndexSize);
- }
-
- // Computes the parser state at the given index
- let insert_rule_context = InsertRuleContext {
- rule_list: &rules.0,
- index,
- };
-
- let state = if nested {
- State::Body
- } else if index == 0 {
- State::Start
- } else {
- insert_rule_context.max_rule_state_at_index(index - 1)
- };
-
- // Steps 3, 4, 5, 6
- CssRule::parse(
- &rule,
- insert_rule_context,
- parent_stylesheet_contents,
- lock,
- state,
- loader,
- allow_import_rules,
- )?
- };
-
- {
- let mut write_guard = lock.write();
- let rules = self.write_with(&mut write_guard);
- rules.0.insert(index, new_rule.clone());
- }
-
- Ok(new_rule)
- }
-}
diff --git a/components/style/stylesheets/rule_parser.rs b/components/style/stylesheets/rule_parser.rs
deleted file mode 100644
index 71b27f2c943..00000000000
--- a/components/style/stylesheets/rule_parser.rs
+++ /dev/null
@@ -1,867 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
-
-//! Parsing of the stylesheet contents.
-
-use crate::counter_style::{parse_counter_style_body, parse_counter_style_name_definition};
-use crate::custom_properties::parse_name as parse_custom_property_name;
-use crate::error_reporting::ContextualParseError;
-use crate::font_face::parse_font_face_block;
-use crate::media_queries::MediaList;
-use crate::parser::{Parse, ParserContext};
-use crate::properties::declaration_block::{
- parse_property_declaration_list, DeclarationParserState, PropertyDeclarationBlock,
-};
-use crate::properties_and_values::rule::{parse_property_block, PropertyRuleName};
-use crate::selector_parser::{SelectorImpl, SelectorParser};
-use crate::shared_lock::{Locked, SharedRwLock};
-use crate::str::starts_with_ignore_ascii_case;
-use crate::stylesheets::container_rule::{ContainerCondition, ContainerRule};
-use crate::stylesheets::document_rule::DocumentCondition;
-use crate::stylesheets::font_feature_values_rule::parse_family_name_list;
-use crate::stylesheets::import_rule::{ImportLayer, ImportRule, ImportSupportsCondition};
-use crate::stylesheets::keyframes_rule::parse_keyframe_list;
-use crate::stylesheets::layer_rule::{LayerBlockRule, LayerName, LayerStatementRule};
-use crate::stylesheets::supports_rule::SupportsCondition;
-use crate::stylesheets::{
- AllowImportRules, CorsMode, CssRule, CssRuleType, CssRules, DocumentRule,
- FontFeatureValuesRule, FontPaletteValuesRule, KeyframesRule, MediaRule, NamespaceRule,
- PageRule, PageSelectors, RulesMutateError, StyleRule, StylesheetLoader, SupportsRule,
-};
-use crate::values::computed::font::FamilyName;
-use crate::values::{CssUrl, CustomIdent, DashedIdent, KeyframesName};
-use crate::{Atom, Namespace, Prefix};
-use cssparser::{
- AtRuleParser, BasicParseError, BasicParseErrorKind, CowRcStr, DeclarationParser, Parser,
- ParserState, QualifiedRuleParser, RuleBodyItemParser, RuleBodyParser, SourceLocation,
- SourcePosition,
-};
-use selectors::SelectorList;
-use servo_arc::Arc;
-use style_traits::{ParseError, StyleParseErrorKind};
-
-/// The information we need particularly to do CSSOM insertRule stuff.
-pub struct InsertRuleContext<'a> {
- /// The rule list we're about to insert into.
- pub rule_list: &'a [CssRule],
- /// The index we're about to get inserted at.
- pub index: usize,
-}
-
-impl<'a> InsertRuleContext<'a> {
- /// Returns the max rule state allowable for insertion at a given index in
- /// the rule list.
- pub fn max_rule_state_at_index(&self, index: usize) -> State {
- let rule = match self.rule_list.get(index) {
- Some(rule) => rule,
- None => return State::Body,
- };
- match rule {
- CssRule::Import(..) => State::Imports,
- CssRule::Namespace(..) => State::Namespaces,
- CssRule::LayerStatement(..) => {
- // If there are @import / @namespace after this layer, then
- // we're in the early-layers phase, otherwise we're in the body
- // and everything is fair game.
- let next_non_layer_statement_rule = self.rule_list[index + 1..]
- .iter()
- .find(|r| !matches!(*r, CssRule::LayerStatement(..)));
- if let Some(non_layer) = next_non_layer_statement_rule {
- if matches!(*non_layer, CssRule::Import(..) | CssRule::Namespace(..)) {
- return State::EarlyLayers;
- }
- }
- State::Body
- },
- _ => State::Body,
- }
- }
-}
-
-/// The parser for the top-level rules in a stylesheet.
-pub struct TopLevelRuleParser<'a, 'i> {
- /// A reference to the lock we need to use to create rules.
- pub shared_lock: &'a SharedRwLock,
- /// A reference to a stylesheet loader if applicable, for `@import` rules.
- pub loader: Option<&'a dyn StylesheetLoader>,
- /// The top-level parser context.
- pub context: ParserContext<'a>,
- /// The current state of the parser.
- pub state: State,
- /// Whether we have tried to parse was invalid due to being in the wrong
- /// place (e.g. an @import rule was found while in the `Body` state). Reset
- /// to `false` when `take_had_hierarchy_error` is called.
- pub dom_error: Option<RulesMutateError>,
- /// The info we need insert a rule in a list.
- pub insert_rule_context: Option<InsertRuleContext<'a>>,
- /// Whether @import rules will be allowed.
- pub allow_import_rules: AllowImportRules,
- /// Parser state for declaration blocks in either nested rules or style rules.
- pub declaration_parser_state: DeclarationParserState<'i>,
- /// The rules we've parsed so far.
- pub rules: Vec<CssRule>,
-}
-
-impl<'a, 'i> TopLevelRuleParser<'a, 'i> {
- fn nested<'b>(&'b mut self) -> NestedRuleParser<'b, 'a, 'i> {
- NestedRuleParser {
- shared_lock: self.shared_lock,
- context: &mut self.context,
- declaration_parser_state: &mut self.declaration_parser_state,
- rules: &mut self.rules,
- }
- }
-
- /// Returns the current state of the parser.
- pub fn state(&self) -> State {
- self.state
- }
-
- /// Checks whether we can parse a rule that would transition us to
- /// `new_state`.
- ///
- /// This is usually a simple branch, but we may need more bookkeeping if
- /// doing `insertRule` from CSSOM.
- fn check_state(&mut self, new_state: State) -> bool {
- if self.state > new_state {
- self.dom_error = Some(RulesMutateError::HierarchyRequest);
- return false;
- }
-
- let ctx = match self.insert_rule_context {
- Some(ref ctx) => ctx,
- None => return true,
- };
-
- let max_rule_state = ctx.max_rule_state_at_index(ctx.index);
- if new_state > max_rule_state {
- self.dom_error = Some(RulesMutateError::HierarchyRequest);
- return false;
- }
-
- // If there's anything that isn't a namespace rule (or import rule, but
- // we checked that already at the beginning), reject with a
- // StateError.
- if new_state == State::Namespaces &&
- ctx.rule_list[ctx.index..]
- .iter()
- .any(|r| !matches!(*r, CssRule::Namespace(..)))
- {
- self.dom_error = Some(RulesMutateError::InvalidState);
- return false;
- }
-
- true
- }
-}
-
-/// The current state of the parser.
-#[derive(Clone, Copy, Eq, Ord, PartialEq, PartialOrd)]
-pub enum State {
- /// We haven't started parsing rules.
- Start = 1,
- /// We're parsing early `@layer` statement rules.
- EarlyLayers = 2,
- /// We're parsing `@import` and early `@layer` statement rules.
- Imports = 3,
- /// We're parsing `@namespace` rules.
- Namespaces = 4,
- /// We're parsing the main body of the stylesheet.
- Body = 5,
-}
-
-#[derive(Clone, Debug, MallocSizeOf, ToShmem)]
-/// Vendor prefix.
-pub enum VendorPrefix {
- /// -moz prefix.
- Moz,
- /// -webkit prefix.
- WebKit,
-}
-
-/// A rule prelude for at-rule with block.
-pub enum AtRulePrelude {
- /// A @font-face rule prelude.
- FontFace,
- /// A @font-feature-values rule prelude, with its FamilyName list.
- FontFeatureValues(Vec<FamilyName>),
- /// A @font-palette-values rule prelude, with its identifier.
- FontPaletteValues(DashedIdent),
- /// A @counter-style rule prelude, with its counter style name.
- CounterStyle(CustomIdent),
- /// A @media rule prelude, with its media queries.
- Media(Arc<Locked<MediaList>>),
- /// A @container rule prelude.
- Container(Arc<ContainerCondition>),
- /// An @supports rule, with its conditional
- Supports(SupportsCondition),
- /// A @keyframes rule, with its animation name and vendor prefix if exists.
- Keyframes(KeyframesName, Option<VendorPrefix>),
- /// A @page rule prelude, with its page name if it exists.
- Page(PageSelectors),
- /// A @property rule prelude.
- Property(PropertyRuleName),
- /// A @document rule, with its conditional.
- Document(DocumentCondition),
- /// A @import rule prelude.
- Import(
- CssUrl,
- Arc<Locked<MediaList>>,
- Option<ImportSupportsCondition>,
- ImportLayer,
- ),
- /// A @namespace rule prelude.
- Namespace(Option<Prefix>, Namespace),
- /// A @layer rule prelude.
- Layer(Vec<LayerName>),
-}
-
-impl<'a, 'i> AtRuleParser<'i> for TopLevelRuleParser<'a, 'i> {
- type Prelude = AtRulePrelude;
- type AtRule = SourcePosition;
- type Error = StyleParseErrorKind<'i>;
-
- fn parse_prelude<'t>(
- &mut self,
- name: CowRcStr<'i>,
- input: &mut Parser<'i, 't>,
- ) -> Result<AtRulePrelude, ParseError<'i>> {
- match_ignore_ascii_case! { &*name,
- "import" => {
- if !self.check_state(State::Imports) {
- return Err(input.new_custom_error(StyleParseErrorKind::UnexpectedImportRule))
- }
-
- if let AllowImportRules::No = self.allow_import_rules {
- return Err(input.new_custom_error(StyleParseErrorKind::DisallowedImportRule))
- }
-
- // FIXME(emilio): We should always be able to have a loader
- // around! See bug 1533783.
- if self.loader.is_none() {
- error!("Saw @import rule, but no way to trigger the load");
- return Err(input.new_custom_error(StyleParseErrorKind::UnexpectedImportRule))
- }
-
- let url_string = input.expect_url_or_string()?.as_ref().to_owned();
- let url = CssUrl::parse_from_string(url_string, &self.context, CorsMode::None);
-
- let (layer, supports) = ImportRule::parse_layer_and_supports(input, &mut self.context);
-
- let media = MediaList::parse(&self.context, input);
- let media = Arc::new(self.shared_lock.wrap(media));
-
- return Ok(AtRulePrelude::Import(url, media, supports, layer));
- },
- "namespace" => {
- if !self.check_state(State::Namespaces) {
- return Err(input.new_custom_error(StyleParseErrorKind::UnexpectedNamespaceRule))
- }
-
- let prefix = input.try_parse(|i| i.expect_ident_cloned())
- .map(|s| Prefix::from(s.as_ref())).ok();
- let maybe_namespace = match input.expect_url_or_string() {
- Ok(url_or_string) => url_or_string,
- Err(BasicParseError { kind: BasicParseErrorKind::UnexpectedToken(t), location }) => {
- return Err(location.new_custom_error(StyleParseErrorKind::UnexpectedTokenWithinNamespace(t)))
- }
- Err(e) => return Err(e.into()),
- };
- let url = Namespace::from(maybe_namespace.as_ref());
- return Ok(AtRulePrelude::Namespace(prefix, url));
- },
- // @charset is removed by rust-cssparser if it’s the first rule in the stylesheet
- // anything left is invalid.
- "charset" => {
- self.dom_error = Some(RulesMutateError::HierarchyRequest);
- return Err(input.new_custom_error(StyleParseErrorKind::UnexpectedCharsetRule))
- },
- "layer" => {
- let state_to_check = if self.state <= State::EarlyLayers {
- // The real state depends on whether there's a block or not.
- // We don't know that yet, but the parse_block check deals
- // with that.
- State::EarlyLayers
- } else {
- State::Body
- };
- if !self.check_state(state_to_check) {
- return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError));
- }
- },
- _ => {
- // All other rules have blocks, so we do this check early in
- // parse_block instead.
- }
- }
-
- AtRuleParser::parse_prelude(&mut self.nested(), name, input)
- }
-
- #[inline]
- fn parse_block<'t>(
- &mut self,
- prelude: AtRulePrelude,
- start: &ParserState,
- input: &mut Parser<'i, 't>,
- ) -> Result<Self::AtRule, ParseError<'i>> {
- if !self.check_state(State::Body) {
- return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError));
- }
- AtRuleParser::parse_block(&mut self.nested(), prelude, start, input)?;
- self.state = State::Body;
- Ok(start.position())
- }
-
- #[inline]
- fn rule_without_block(
- &mut self,
- prelude: AtRulePrelude,
- start: &ParserState,
- ) -> Result<Self::AtRule, ()> {
- match prelude {
- AtRulePrelude::Import(url, media, supports, layer) => {
- let loader = self
- .loader
- .expect("Expected a stylesheet loader for @import");
-
- let import_rule = loader.request_stylesheet(
- url,
- start.source_location(),
- &self.context,
- &self.shared_lock,
- media,
- supports,
- layer,
- );
-
- self.state = State::Imports;
- self.rules.push(CssRule::Import(import_rule))
- },
- AtRulePrelude::Namespace(prefix, url) => {
- let namespaces = self.context.namespaces.to_mut();
- let prefix = if let Some(prefix) = prefix {
- namespaces.prefixes.insert(prefix.clone(), url.clone());
- Some(prefix)
- } else {
- namespaces.default = Some(url.clone());
- None
- };
-
- self.state = State::Namespaces;
- self.rules.push(CssRule::Namespace(Arc::new(NamespaceRule {
- prefix,
- url,
- source_location: start.source_location(),
- })));
- },
- AtRulePrelude::Layer(..) => {
- AtRuleParser::rule_without_block(&mut self.nested(), prelude, start)?;
- if self.state <= State::EarlyLayers {
- self.state = State::EarlyLayers;
- } else {
- self.state = State::Body;
- }
- },
- _ => AtRuleParser::rule_without_block(&mut self.nested(), prelude, start)?,
- };
-
- Ok(start.position())
- }
-}
-
-impl<'a, 'i> QualifiedRuleParser<'i> for TopLevelRuleParser<'a, 'i> {
- type Prelude = SelectorList<SelectorImpl>;
- type QualifiedRule = SourcePosition;
- type Error = StyleParseErrorKind<'i>;
-
- #[inline]
- fn parse_prelude<'t>(
- &mut self,
- input: &mut Parser<'i, 't>,
- ) -> Result<Self::Prelude, ParseError<'i>> {
- if !self.check_state(State::Body) {
- return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError));
- }
-
- QualifiedRuleParser::parse_prelude(&mut self.nested(), input)
- }
-
- #[inline]
- fn parse_block<'t>(
- &mut self,
- prelude: Self::Prelude,
- start: &ParserState,
- input: &mut Parser<'i, 't>,
- ) -> Result<Self::QualifiedRule, ParseError<'i>> {
- QualifiedRuleParser::parse_block(&mut self.nested(), prelude, start, input)?;
- self.state = State::Body;
- Ok(start.position())
- }
-}
-
-struct NestedRuleParser<'a, 'b: 'a, 'i> {
- shared_lock: &'a SharedRwLock,
- context: &'a mut ParserContext<'b>,
- declaration_parser_state: &'a mut DeclarationParserState<'i>,
- rules: &'a mut Vec<CssRule>,
-}
-
-struct NestedParseResult {
- rules: Vec<CssRule>,
- declarations: PropertyDeclarationBlock,
-}
-
-impl NestedParseResult {
- fn into_rules(
- mut self,
- shared_lock: &SharedRwLock,
- source_location: SourceLocation,
- ) -> Arc<Locked<CssRules>> {
- lazy_static! {
- static ref AMPERSAND: SelectorList<SelectorImpl> = {
- let list = SelectorList::ampersand();
- list.0
- .iter()
- .for_each(|selector| selector.mark_as_intentionally_leaked());
- list
- };
- };
-
- if !self.declarations.is_empty() {
- self.rules.insert(
- 0,
- CssRule::Style(Arc::new(shared_lock.wrap(StyleRule {
- selectors: AMPERSAND.clone(),
- block: Arc::new(shared_lock.wrap(self.declarations)),
- rules: None,
- source_location,
- }))),
- )
- }
-
- CssRules::new(self.rules, shared_lock)
- }
-}
-
-impl<'a, 'b, 'i> NestedRuleParser<'a, 'b, 'i> {
- /// When nesting is disabled, we prevent parsing at rules and qualified rules inside style
- /// rules.
- fn allow_at_and_qualified_rules(&self) -> bool {
- if !self.context.rule_types.contains(CssRuleType::Style) {
- return true;
- }
- static_prefs::pref!("layout.css.nesting.enabled")
- }
-
- fn nest_for_rule<R>(&mut self, rule_type: CssRuleType, cb: impl FnOnce(&mut Self) -> R) -> R {
- let old_rule_types = self.context.rule_types;
- self.context.rule_types.insert(rule_type);
- let r = cb(self);
- self.context.rule_types = old_rule_types;
- r
- }
-
- fn parse_nested(
- &mut self,
- input: &mut Parser<'i, '_>,
- rule_type: CssRuleType,
- selectors: Option<&SelectorList<SelectorImpl>>,
- ) -> NestedParseResult {
- self.nest_for_rule(rule_type, |parser| {
- let parse_declarations = parser.parse_declarations();
- let mut old_declaration_state = std::mem::take(parser.declaration_parser_state);
- let mut rules = std::mem::take(parser.rules);
- let mut iter = RuleBodyParser::new(input, parser);
- while let Some(result) = iter.next() {
- match result {
- Ok(()) => {},
- Err((error, slice)) => {
- if parse_declarations {
- iter.parser.declaration_parser_state.did_error(
- iter.parser.context,
- error,
- slice,
- );
- } else {
- let location = error.location;
- let error = ContextualParseError::InvalidRule(slice, error);
- iter.parser.context.log_css_error(location, error);
- }
- },
- }
- }
- let declarations = if parse_declarations {
- parser
- .declaration_parser_state
- .report_errors_if_needed(parser.context, selectors);
- parser.declaration_parser_state.take_declarations()
- } else {
- PropertyDeclarationBlock::default()
- };
- debug_assert!(
- !parser.declaration_parser_state.has_parsed_declarations(),
- "Parsed but didn't consume declarations"
- );
- std::mem::swap(parser.declaration_parser_state, &mut old_declaration_state);
- std::mem::swap(parser.rules, &mut rules);
- NestedParseResult {
- rules,
- declarations,
- }
- })
- }
-}
-
-impl<'a, 'b, 'i> AtRuleParser<'i> for NestedRuleParser<'a, 'b, 'i> {
- type Prelude = AtRulePrelude;
- type AtRule = ();
- type Error = StyleParseErrorKind<'i>;
-
- fn parse_prelude<'t>(
- &mut self,
- name: CowRcStr<'i>,
- input: &mut Parser<'i, 't>,
- ) -> Result<Self::Prelude, ParseError<'i>> {
- if !self.allow_at_and_qualified_rules() {
- return Err(input.new_error(BasicParseErrorKind::AtRuleInvalid(name)));
- }
- Ok(match_ignore_ascii_case! { &*name,
- "media" => {
- let media_queries = MediaList::parse(self.context, input);
- let arc = Arc::new(self.shared_lock.wrap(media_queries));
- AtRulePrelude::Media(arc)
- },
- "supports" => {
- let cond = SupportsCondition::parse(input)?;
- AtRulePrelude::Supports(cond)
- },
- "font-face" => {
- AtRulePrelude::FontFace
- },
- "container" if static_prefs::pref!("layout.css.container-queries.enabled") => {
- let condition = Arc::new(ContainerCondition::parse(self.context, input)?);
- AtRulePrelude::Container(condition)
- },
- "layer" => {
- let names = input.try_parse(|input| {
- input.parse_comma_separated(|input| {
- LayerName::parse(self.context, input)
- })
- }).unwrap_or_default();
- AtRulePrelude::Layer(names)
- },
- "font-feature-values" if cfg!(feature = "gecko") => {
- let family_names = parse_family_name_list(self.context, input)?;
- AtRulePrelude::FontFeatureValues(family_names)
- },
- "font-palette-values" if static_prefs::pref!("layout.css.font-palette.enabled") => {
- let name = DashedIdent::parse(self.context, input)?;
- AtRulePrelude::FontPaletteValues(name)
- },
- "counter-style" if cfg!(feature = "gecko") => {
- let name = parse_counter_style_name_definition(input)?;
- AtRulePrelude::CounterStyle(name)
- },
- "keyframes" | "-webkit-keyframes" | "-moz-keyframes" => {
- let prefix = if starts_with_ignore_ascii_case(&*name, "-webkit-") {
- Some(VendorPrefix::WebKit)
- } else if starts_with_ignore_ascii_case(&*name, "-moz-") {
- Some(VendorPrefix::Moz)
- } else {
- None
- };
- if cfg!(feature = "servo") &&
- prefix.as_ref().map_or(false, |p| matches!(*p, VendorPrefix::Moz)) {
- // Servo should not support @-moz-keyframes.
- return Err(input.new_error(BasicParseErrorKind::AtRuleInvalid(name.clone())))
- }
- let name = KeyframesName::parse(self.context, input)?;
- AtRulePrelude::Keyframes(name, prefix)
- },
- "page" if cfg!(feature = "gecko") => {
- AtRulePrelude::Page(
- input.try_parse(|i| PageSelectors::parse(self.context, i)).unwrap_or_default()
- )
- },
- "property" if static_prefs::pref!("layout.css.properties-and-values.enabled") => {
- let name = input.expect_ident_cloned()?;
- let name = parse_custom_property_name(&name).map_err(|_| {
- input.new_custom_error(StyleParseErrorKind::UnexpectedIdent(name.clone()))
- })?;
- AtRulePrelude::Property(PropertyRuleName(Arc::new(Atom::from(name))))
- },
- "-moz-document" if cfg!(feature = "gecko") => {
- let cond = DocumentCondition::parse(self.context, input)?;
- AtRulePrelude::Document(cond)
- },
- _ => return Err(input.new_error(BasicParseErrorKind::AtRuleInvalid(name.clone())))
- })
- }
-
- fn parse_block<'t>(
- &mut self,
- prelude: AtRulePrelude,
- start: &ParserState,
- input: &mut Parser<'i, 't>,
- ) -> Result<(), ParseError<'i>> {
- let rule = match prelude {
- AtRulePrelude::FontFace => self.nest_for_rule(CssRuleType::FontFace, |p| {
- CssRule::FontFace(Arc::new(p.shared_lock.wrap(
- parse_font_face_block(&p.context, input, start.source_location()).into(),
- )))
- }),
- AtRulePrelude::FontFeatureValues(family_names) => {
- self.nest_for_rule(CssRuleType::FontFeatureValues, |p| {
- CssRule::FontFeatureValues(Arc::new(FontFeatureValuesRule::parse(
- &p.context,
- input,
- family_names,
- start.source_location(),
- )))
- })
- },
- AtRulePrelude::FontPaletteValues(name) => {
- self.nest_for_rule(CssRuleType::FontPaletteValues, |p| {
- CssRule::FontPaletteValues(Arc::new(FontPaletteValuesRule::parse(
- &p.context,
- input,
- name,
- start.source_location(),
- )))
- })
- },
- AtRulePrelude::CounterStyle(name) => {
- let body = self.nest_for_rule(CssRuleType::CounterStyle, |p| {
- parse_counter_style_body(name, &p.context, input, start.source_location())
- })?;
- CssRule::CounterStyle(Arc::new(self.shared_lock.wrap(body)))
- },
- AtRulePrelude::Media(media_queries) => {
- let source_location = start.source_location();
- CssRule::Media(Arc::new(MediaRule {
- media_queries,
- rules: self
- .parse_nested(input, CssRuleType::Media, None)
- .into_rules(self.shared_lock, source_location),
- source_location,
- }))
- },
- AtRulePrelude::Supports(condition) => {
- let enabled =
- self.nest_for_rule(CssRuleType::Style, |p| condition.eval(&p.context));
- let source_location = start.source_location();
- CssRule::Supports(Arc::new(SupportsRule {
- condition,
- rules: self
- .parse_nested(input, CssRuleType::Supports, None)
- .into_rules(self.shared_lock, source_location),
- enabled,
- source_location,
- }))
- },
- AtRulePrelude::Keyframes(name, vendor_prefix) => {
- self.nest_for_rule(CssRuleType::Keyframe, |p| {
- CssRule::Keyframes(Arc::new(p.shared_lock.wrap(KeyframesRule {
- name,
- keyframes: parse_keyframe_list(&mut p.context, input, p.shared_lock),
- vendor_prefix,
- source_location: start.source_location(),
- })))
- })
- },
- AtRulePrelude::Page(selectors) => {
- let declarations = self.nest_for_rule(CssRuleType::Page, |p| {
- // TODO: Support nesting in @page rules?
- parse_property_declaration_list(&p.context, input, None)
- });
- CssRule::Page(Arc::new(self.shared_lock.wrap(PageRule {
- selectors,
- block: Arc::new(self.shared_lock.wrap(declarations)),
- source_location: start.source_location(),
- })))
- },
- AtRulePrelude::Property(name) => self.nest_for_rule(CssRuleType::Property, |p| {
- CssRule::Property(Arc::new(parse_property_block(
- &p.context,
- input,
- name,
- start.source_location(),
- )))
- }),
- AtRulePrelude::Document(condition) => {
- if !cfg!(feature = "gecko") {
- unreachable!()
- }
- let source_location = start.source_location();
- CssRule::Document(Arc::new(DocumentRule {
- condition,
- rules: self
- .parse_nested(input, CssRuleType::Document, None)
- .into_rules(self.shared_lock, source_location),
- source_location,
- }))
- },
- AtRulePrelude::Container(condition) => {
- let source_location = start.source_location();
- CssRule::Container(Arc::new(ContainerRule {
- condition,
- rules: self
- .parse_nested(input, CssRuleType::Container, None)
- .into_rules(self.shared_lock, source_location),
- source_location,
- }))
- },
- AtRulePrelude::Layer(names) => {
- let name = match names.len() {
- 0 | 1 => names.into_iter().next(),
- _ => return Err(input.new_error(BasicParseErrorKind::AtRuleBodyInvalid)),
- };
- let source_location = start.source_location();
- CssRule::LayerBlock(Arc::new(LayerBlockRule {
- name,
- rules: self
- .parse_nested(input, CssRuleType::LayerBlock, None)
- .into_rules(self.shared_lock, source_location),
- source_location,
- }))
- },
- AtRulePrelude::Import(..) | AtRulePrelude::Namespace(..) => {
- // These rules don't have blocks.
- return Err(input.new_unexpected_token_error(cssparser::Token::CurlyBracketBlock));
- },
- };
- self.rules.push(rule);
- Ok(())
- }
-
- #[inline]
- fn rule_without_block(
- &mut self,
- prelude: AtRulePrelude,
- start: &ParserState,
- ) -> Result<(), ()> {
- let rule = match prelude {
- AtRulePrelude::Layer(names) => {
- if names.is_empty() {
- return Err(());
- }
- CssRule::LayerStatement(Arc::new(LayerStatementRule {
- names,
- source_location: start.source_location(),
- }))
- },
- _ => return Err(()),
- };
- self.rules.push(rule);
- Ok(())
- }
-}
-
-#[inline(never)]
-fn check_for_useless_selector(
- input: &mut Parser,
- context: &ParserContext,
- selectors: &SelectorList<SelectorImpl>,
-) {
- use cssparser::ToCss;
-
- 'selector_loop: for selector in selectors.0.iter() {
- let mut current = selector.iter();
- loop {
- let mut found_host = false;
- let mut found_non_host = false;
- for component in &mut current {
- if component.is_host() {
- found_host = true;
- } else {
- found_non_host = true;
- }
- if found_host && found_non_host {
- let location = input.current_source_location();
- context.log_css_error(
- location,
- ContextualParseError::NeverMatchingHostSelector(selector.to_css_string()),
- );
- continue 'selector_loop;
- }
- }
- if current.next_sequence().is_none() {
- break;
- }
- }
- }
-}
-
-impl<'a, 'b, 'i> QualifiedRuleParser<'i> for NestedRuleParser<'a, 'b, 'i> {
- type Prelude = SelectorList<SelectorImpl>;
- type QualifiedRule = ();
- type Error = StyleParseErrorKind<'i>;
-
- fn parse_prelude<'t>(
- &mut self,
- input: &mut Parser<'i, 't>,
- ) -> Result<Self::Prelude, ParseError<'i>> {
- let selector_parser = SelectorParser {
- stylesheet_origin: self.context.stylesheet_origin,
- namespaces: &self.context.namespaces,
- url_data: self.context.url_data,
- for_supports_rule: false,
- };
- let selectors = SelectorList::parse(&selector_parser, input)?;
- if self.context.error_reporting_enabled() {
- check_for_useless_selector(input, &self.context, &selectors);
- }
- Ok(selectors)
- }
-
- fn parse_block<'t>(
- &mut self,
- selectors: Self::Prelude,
- start: &ParserState,
- input: &mut Parser<'i, 't>,
- ) -> Result<(), ParseError<'i>> {
- let result = self.parse_nested(input, CssRuleType::Style, Some(&selectors));
- let block = Arc::new(self.shared_lock.wrap(result.declarations));
- self.rules
- .push(CssRule::Style(Arc::new(self.shared_lock.wrap(StyleRule {
- selectors,
- block,
- rules: if result.rules.is_empty() {
- None
- } else {
- Some(CssRules::new(result.rules, self.shared_lock))
- },
- source_location: start.source_location(),
- }))));
- Ok(())
- }
-}
-
-impl<'a, 'b, 'i> DeclarationParser<'i> for NestedRuleParser<'a, 'b, 'i> {
- type Declaration = ();
- type Error = StyleParseErrorKind<'i>;
- fn parse_value<'t>(
- &mut self,
- name: CowRcStr<'i>,
- input: &mut Parser<'i, 't>,
- ) -> Result<(), ParseError<'i>> {
- self.declaration_parser_state
- .parse_value(self.context, name, input)
- }
-}
-
-impl<'a, 'b, 'i> RuleBodyItemParser<'i, (), StyleParseErrorKind<'i>>
- for NestedRuleParser<'a, 'b, 'i>
-{
- fn parse_qualified(&self) -> bool {
- self.allow_at_and_qualified_rules()
- }
-
- /// If nesting is disabled, we can't get there for a non-style-rule. If it's enabled, we parse
- /// raw declarations there.
- fn parse_declarations(&self) -> bool {
- self.context.rule_types.contains(CssRuleType::Style)
- }
-}
diff --git a/components/style/stylesheets/rules_iterator.rs b/components/style/stylesheets/rules_iterator.rs
deleted file mode 100644
index 0ab2b42c0bc..00000000000
--- a/components/style/stylesheets/rules_iterator.rs
+++ /dev/null
@@ -1,326 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
-
-//! An iterator over a list of rules.
-
-use crate::context::QuirksMode;
-use crate::media_queries::Device;
-use crate::shared_lock::SharedRwLockReadGuard;
-use crate::stylesheets::{CssRule, DocumentRule, ImportRule, MediaRule, SupportsRule};
-use smallvec::SmallVec;
-use std::slice;
-
-/// An iterator over a list of rules.
-pub struct RulesIterator<'a, 'b, C>
-where
- 'b: 'a,
- C: NestedRuleIterationCondition + 'static,
-{
- device: &'a Device,
- quirks_mode: QuirksMode,
- guard: &'a SharedRwLockReadGuard<'b>,
- stack: SmallVec<[slice::Iter<'a, CssRule>; 3]>,
- _phantom: ::std::marker::PhantomData<C>,
-}
-
-impl<'a, 'b, C> RulesIterator<'a, 'b, C>
-where
- 'b: 'a,
- C: NestedRuleIterationCondition + 'static,
-{
- /// Creates a new `RulesIterator` to iterate over `rules`.
- pub fn new(
- device: &'a Device,
- quirks_mode: QuirksMode,
- guard: &'a SharedRwLockReadGuard<'b>,
- rules: slice::Iter<'a, CssRule>,
- ) -> Self {
- let mut stack = SmallVec::new();
- stack.push(rules);
- Self {
- device,
- quirks_mode,
- guard,
- stack,
- _phantom: ::std::marker::PhantomData,
- }
- }
-
- /// Skips all the remaining children of the last nested rule processed.
- pub fn skip_children(&mut self) {
- self.stack.pop();
- }
-
- /// Returns the children of `rule`, and whether `rule` is effective.
- pub fn children(
- rule: &'a CssRule,
- device: &'a Device,
- quirks_mode: QuirksMode,
- guard: &'a SharedRwLockReadGuard<'_>,
- effective: &mut bool,
- ) -> Option<slice::Iter<'a, CssRule>> {
- *effective = true;
- match *rule {
- CssRule::Namespace(_) |
- CssRule::FontFace(_) |
- CssRule::CounterStyle(_) |
- CssRule::Keyframes(_) |
- CssRule::Page(_) |
- CssRule::Property(_) |
- CssRule::LayerStatement(_) |
- CssRule::FontFeatureValues(_) |
- CssRule::FontPaletteValues(_) => None,
- CssRule::Style(ref style_rule) => {
- let style_rule = style_rule.read_with(guard);
- style_rule
- .rules
- .as_ref()
- .map(|r| r.read_with(guard).0.iter())
- },
- CssRule::Import(ref import_rule) => {
- let import_rule = import_rule.read_with(guard);
- if !C::process_import(guard, device, quirks_mode, import_rule) {
- *effective = false;
- return None;
- }
- Some(import_rule.stylesheet.rules(guard).iter())
- },
- CssRule::Document(ref doc_rule) => {
- if !C::process_document(guard, device, quirks_mode, doc_rule) {
- *effective = false;
- return None;
- }
- Some(doc_rule.rules.read_with(guard).0.iter())
- },
- CssRule::Container(ref container_rule) => {
- Some(container_rule.rules.read_with(guard).0.iter())
- },
- CssRule::Media(ref media_rule) => {
- if !C::process_media(guard, device, quirks_mode, media_rule) {
- *effective = false;
- return None;
- }
- Some(media_rule.rules.read_with(guard).0.iter())
- },
- CssRule::Supports(ref supports_rule) => {
- if !C::process_supports(guard, device, quirks_mode, supports_rule) {
- *effective = false;
- return None;
- }
- Some(supports_rule.rules.read_with(guard).0.iter())
- },
- CssRule::LayerBlock(ref layer_rule) => Some(layer_rule.rules.read_with(guard).0.iter()),
- }
- }
-}
-
-impl<'a, 'b, C> Iterator for RulesIterator<'a, 'b, C>
-where
- 'b: 'a,
- C: NestedRuleIterationCondition + 'static,
-{
- type Item = &'a CssRule;
-
- fn next(&mut self) -> Option<Self::Item> {
- while !self.stack.is_empty() {
- let rule = {
- let nested_iter = self.stack.last_mut().unwrap();
- match nested_iter.next() {
- Some(r) => r,
- None => {
- self.stack.pop();
- continue;
- },
- }
- };
-
- let mut effective = true;
- let children = Self::children(
- rule,
- self.device,
- self.quirks_mode,
- self.guard,
- &mut effective,
- );
- if !effective {
- continue;
- }
-
- if let Some(children) = children {
- // NOTE: It's important that `children` gets pushed even if
- // empty, so that `skip_children()` works as expected.
- self.stack.push(children);
- }
-
- return Some(rule);
- }
-
- None
- }
-}
-
-/// RulesIterator.
-pub trait NestedRuleIterationCondition {
- /// Whether we should process the nested rules in a given `@import` rule.
- fn process_import(
- guard: &SharedRwLockReadGuard,
- device: &Device,
- quirks_mode: QuirksMode,
- rule: &ImportRule,
- ) -> bool;
-
- /// Whether we should process the nested rules in a given `@media` rule.
- fn process_media(
- guard: &SharedRwLockReadGuard,
- device: &Device,
- quirks_mode: QuirksMode,
- rule: &MediaRule,
- ) -> bool;
-
- /// Whether we should process the nested rules in a given `@-moz-document`
- /// rule.
- fn process_document(
- guard: &SharedRwLockReadGuard,
- device: &Device,
- quirks_mode: QuirksMode,
- rule: &DocumentRule,
- ) -> bool;
-
- /// Whether we should process the nested rules in a given `@supports` rule.
- fn process_supports(
- guard: &SharedRwLockReadGuard,
- device: &Device,
- quirks_mode: QuirksMode,
- rule: &SupportsRule,
- ) -> bool;
-}
-
-/// A struct that represents the condition that a rule applies to the document.
-pub struct EffectiveRules;
-
-impl EffectiveRules {
- /// Returns whether a given rule is effective.
- pub fn is_effective(
- guard: &SharedRwLockReadGuard,
- device: &Device,
- quirks_mode: QuirksMode,
- rule: &CssRule,
- ) -> bool {
- match *rule {
- CssRule::Import(ref import_rule) => {
- let import_rule = import_rule.read_with(guard);
- Self::process_import(guard, device, quirks_mode, import_rule)
- },
- CssRule::Document(ref doc_rule) => {
- Self::process_document(guard, device, quirks_mode, doc_rule)
- },
- CssRule::Media(ref media_rule) => {
- Self::process_media(guard, device, quirks_mode, media_rule)
- },
- CssRule::Supports(ref supports_rule) => {
- Self::process_supports(guard, device, quirks_mode, supports_rule)
- },
- _ => true,
- }
- }
-}
-
-impl NestedRuleIterationCondition for EffectiveRules {
- fn process_import(
- guard: &SharedRwLockReadGuard,
- device: &Device,
- quirks_mode: QuirksMode,
- rule: &ImportRule,
- ) -> bool {
- match rule.stylesheet.media(guard) {
- Some(m) => m.evaluate(device, quirks_mode),
- None => true,
- }
- }
-
- fn process_media(
- guard: &SharedRwLockReadGuard,
- device: &Device,
- quirks_mode: QuirksMode,
- rule: &MediaRule,
- ) -> bool {
- rule.media_queries
- .read_with(guard)
- .evaluate(device, quirks_mode)
- }
-
- fn process_document(
- _: &SharedRwLockReadGuard,
- device: &Device,
- _: QuirksMode,
- rule: &DocumentRule,
- ) -> bool {
- rule.condition.evaluate(device)
- }
-
- fn process_supports(
- _: &SharedRwLockReadGuard,
- _: &Device,
- _: QuirksMode,
- rule: &SupportsRule,
- ) -> bool {
- rule.enabled
- }
-}
-
-/// A filter that processes all the rules in a rule list.
-pub struct AllRules;
-
-impl NestedRuleIterationCondition for AllRules {
- fn process_import(
- _: &SharedRwLockReadGuard,
- _: &Device,
- _: QuirksMode,
- _: &ImportRule,
- ) -> bool {
- true
- }
-
- fn process_media(_: &SharedRwLockReadGuard, _: &Device, _: QuirksMode, _: &MediaRule) -> bool {
- true
- }
-
- fn process_document(
- _: &SharedRwLockReadGuard,
- _: &Device,
- _: QuirksMode,
- _: &DocumentRule,
- ) -> bool {
- true
- }
-
- fn process_supports(
- _: &SharedRwLockReadGuard,
- _: &Device,
- _: QuirksMode,
- _: &SupportsRule,
- ) -> bool {
- true
- }
-}
-
-/// An iterator over all the effective rules of a stylesheet.
-///
-/// NOTE: This iterator recurses into `@import` rules.
-pub type EffectiveRulesIterator<'a, 'b> = RulesIterator<'a, 'b, EffectiveRules>;
-
-impl<'a, 'b> EffectiveRulesIterator<'a, 'b> {
- /// Returns an iterator over the effective children of a rule, even if
- /// `rule` itself is not effective.
- pub fn effective_children(
- device: &'a Device,
- quirks_mode: QuirksMode,
- guard: &'a SharedRwLockReadGuard<'b>,
- rule: &'a CssRule,
- ) -> Self {
- let children =
- RulesIterator::<AllRules>::children(rule, device, quirks_mode, guard, &mut false);
- EffectiveRulesIterator::new(device, quirks_mode, guard, children.unwrap_or([].iter()))
- }
-}
diff --git a/components/style/stylesheets/style_rule.rs b/components/style/stylesheets/style_rule.rs
deleted file mode 100644
index d5a22d6fc20..00000000000
--- a/components/style/stylesheets/style_rule.rs
+++ /dev/null
@@ -1,104 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
-
-//! A style rule.
-
-use crate::properties::PropertyDeclarationBlock;
-use crate::selector_parser::SelectorImpl;
-use crate::shared_lock::{
- DeepCloneParams, DeepCloneWithLock, Locked, SharedRwLock, SharedRwLockReadGuard, ToCssWithGuard,
-};
-use crate::str::CssStringWriter;
-use crate::stylesheets::CssRules;
-use cssparser::SourceLocation;
-#[cfg(feature = "gecko")]
-use malloc_size_of::MallocUnconditionalShallowSizeOf;
-#[cfg(feature = "gecko")]
-use malloc_size_of::{MallocSizeOf, MallocSizeOfOps};
-use selectors::SelectorList;
-use servo_arc::Arc;
-use std::fmt::{self, Write};
-
-/// A style rule, with selectors and declarations.
-#[derive(Debug, ToShmem)]
-pub struct StyleRule {
- /// The list of selectors in this rule.
- pub selectors: SelectorList<SelectorImpl>,
- /// The declaration block with the properties it contains.
- pub block: Arc<Locked<PropertyDeclarationBlock>>,
- /// The nested rules to this style rule. Only non-`None` when nesting is enabled.
- pub rules: Option<Arc<Locked<CssRules>>>,
- /// The location in the sheet where it was found.
- pub source_location: SourceLocation,
-}
-
-impl DeepCloneWithLock for StyleRule {
- /// Deep clones this StyleRule.
- fn deep_clone_with_lock(
- &self,
- lock: &SharedRwLock,
- guard: &SharedRwLockReadGuard,
- params: &DeepCloneParams,
- ) -> StyleRule {
- StyleRule {
- selectors: self.selectors.clone(),
- block: Arc::new(lock.wrap(self.block.read_with(guard).clone())),
- rules: self.rules.as_ref().map(|rules| {
- let rules = rules.read_with(guard);
- Arc::new(lock.wrap(rules.deep_clone_with_lock(lock, guard, params)))
- }),
- source_location: self.source_location.clone(),
- }
- }
-}
-
-impl StyleRule {
- /// Measure heap usage.
- #[cfg(feature = "gecko")]
- pub fn size_of(&self, guard: &SharedRwLockReadGuard, ops: &mut MallocSizeOfOps) -> usize {
- let mut n = 0;
- n += self.selectors.0.size_of(ops);
- n += self.block.unconditional_shallow_size_of(ops) +
- self.block.read_with(guard).size_of(ops);
- if let Some(ref rules) = self.rules {
- n += rules.unconditional_shallow_size_of(ops) +
- rules.read_with(guard).size_of(guard, ops)
- }
- n
- }
-}
-
-impl ToCssWithGuard for StyleRule {
- /// https://drafts.csswg.org/cssom/#serialize-a-css-rule CSSStyleRule
- fn to_css(&self, guard: &SharedRwLockReadGuard, dest: &mut CssStringWriter) -> fmt::Result {
- use cssparser::ToCss;
- // Step 1
- self.selectors.to_css(dest)?;
- dest.write_str(" {")?;
-
- // Step 2
- let declaration_block = self.block.read_with(guard);
- let has_declarations = !declaration_block.declarations().is_empty();
-
- // Step 3
- if let Some(ref rules) = self.rules {
- let rules = rules.read_with(guard);
- // Step 6 (here because it's more convenient)
- if !rules.is_empty() {
- if has_declarations {
- dest.write_str("\n ")?;
- declaration_block.to_css(dest)?;
- }
- return rules.to_css_block_without_opening(guard, dest);
- }
- }
-
- // Steps 4 & 5
- if has_declarations {
- dest.write_char(' ')?;
- declaration_block.to_css(dest)?;
- }
- dest.write_str(" }")
- }
-}
diff --git a/components/style/stylesheets/stylesheet.rs b/components/style/stylesheets/stylesheet.rs
deleted file mode 100644
index 6c205e03c66..00000000000
--- a/components/style/stylesheets/stylesheet.rs
+++ /dev/null
@@ -1,605 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
-
-use crate::context::QuirksMode;
-use crate::error_reporting::{ContextualParseError, ParseErrorReporter};
-use crate::media_queries::{Device, MediaList};
-use crate::parser::ParserContext;
-use crate::shared_lock::{DeepCloneParams, DeepCloneWithLock, Locked};
-use crate::shared_lock::{SharedRwLock, SharedRwLockReadGuard};
-use crate::stylesheets::loader::StylesheetLoader;
-use crate::stylesheets::rule_parser::{State, TopLevelRuleParser};
-use crate::stylesheets::rules_iterator::{EffectiveRules, EffectiveRulesIterator};
-use crate::stylesheets::rules_iterator::{NestedRuleIterationCondition, RulesIterator};
-use crate::stylesheets::{CssRule, CssRules, Origin, UrlExtraData};
-use crate::use_counters::UseCounters;
-use crate::{Namespace, Prefix};
-use cssparser::{Parser, ParserInput, StyleSheetParser};
-use fxhash::FxHashMap;
-#[cfg(feature = "gecko")]
-use malloc_size_of::{MallocSizeOfOps, MallocUnconditionalShallowSizeOf};
-use parking_lot::RwLock;
-use servo_arc::Arc;
-use std::sync::atomic::{AtomicBool, Ordering};
-use style_traits::ParsingMode;
-
-/// This structure holds the user-agent and user stylesheets.
-pub struct UserAgentStylesheets {
- /// The lock used for user-agent stylesheets.
- pub shared_lock: SharedRwLock,
- /// The user or user agent stylesheets.
- pub user_or_user_agent_stylesheets: Vec<DocumentStyleSheet>,
- /// The quirks mode stylesheet.
- pub quirks_mode_stylesheet: DocumentStyleSheet,
-}
-
-/// A set of namespaces applying to a given stylesheet.
-///
-/// The namespace id is used in gecko
-#[derive(Clone, Debug, Default, MallocSizeOf)]
-#[allow(missing_docs)]
-pub struct Namespaces {
- pub default: Option<Namespace>,
- pub prefixes: FxHashMap<Prefix, Namespace>,
-}
-
-/// The contents of a given stylesheet. This effectively maps to a
-/// StyleSheetInner in Gecko.
-#[derive(Debug)]
-pub struct StylesheetContents {
- /// List of rules in the order they were found (important for
- /// cascading order)
- pub rules: Arc<Locked<CssRules>>,
- /// The origin of this stylesheet.
- pub origin: Origin,
- /// The url data this stylesheet should use.
- pub url_data: RwLock<UrlExtraData>,
- /// The namespaces that apply to this stylesheet.
- pub namespaces: RwLock<Namespaces>,
- /// The quirks mode of this stylesheet.
- pub quirks_mode: QuirksMode,
- /// This stylesheet's source map URL.
- pub source_map_url: RwLock<Option<String>>,
- /// This stylesheet's source URL.
- pub source_url: RwLock<Option<String>>,
-
- /// We don't want to allow construction outside of this file, to guarantee
- /// that all contents are created with Arc<>.
- _forbid_construction: (),
-}
-
-impl StylesheetContents {
- /// Parse a given CSS string, with a given url-data, origin, and
- /// quirks mode.
- pub fn from_str(
- css: &str,
- url_data: UrlExtraData,
- origin: Origin,
- shared_lock: &SharedRwLock,
- stylesheet_loader: Option<&dyn StylesheetLoader>,
- error_reporter: Option<&dyn ParseErrorReporter>,
- quirks_mode: QuirksMode,
- line_number_offset: u32,
- use_counters: Option<&UseCounters>,
- allow_import_rules: AllowImportRules,
- sanitization_data: Option<&mut SanitizationData>,
- ) -> Arc<Self> {
- let (namespaces, rules, source_map_url, source_url) = Stylesheet::parse_rules(
- css,
- &url_data,
- origin,
- &shared_lock,
- stylesheet_loader,
- error_reporter,
- quirks_mode,
- line_number_offset,
- use_counters,
- allow_import_rules,
- sanitization_data,
- );
-
- Arc::new(Self {
- rules: CssRules::new(rules, &shared_lock),
- origin,
- url_data: RwLock::new(url_data),
- namespaces: RwLock::new(namespaces),
- quirks_mode,
- source_map_url: RwLock::new(source_map_url),
- source_url: RwLock::new(source_url),
- _forbid_construction: (),
- })
- }
-
- /// Creates a new StylesheetContents with the specified pre-parsed rules,
- /// origin, URL data, and quirks mode.
- ///
- /// Since the rules have already been parsed, and the intention is that
- /// this function is used for read only User Agent style sheets, an empty
- /// namespace map is used, and the source map and source URLs are set to
- /// None.
- ///
- /// An empty namespace map should be fine, as it is only used for parsing,
- /// not serialization of existing selectors. Since UA sheets are read only,
- /// we should never need the namespace map.
- pub fn from_data(
- rules: Arc<Locked<CssRules>>,
- origin: Origin,
- url_data: UrlExtraData,
- quirks_mode: QuirksMode,
- ) -> Arc<Self> {
- Arc::new(Self {
- rules,
- origin,
- url_data: RwLock::new(url_data),
- namespaces: RwLock::new(Namespaces::default()),
- quirks_mode,
- source_map_url: RwLock::new(None),
- source_url: RwLock::new(None),
- _forbid_construction: (),
- })
- }
-
- /// Same as above, but ensuring that the rules are static.
- pub fn from_shared_data(
- rules: Arc<Locked<CssRules>>,
- origin: Origin,
- url_data: UrlExtraData,
- quirks_mode: QuirksMode,
- ) -> Arc<Self> {
- debug_assert!(rules.is_static());
- Self::from_data(rules, origin, url_data, quirks_mode)
- }
-
- /// Returns a reference to the list of rules.
- #[inline]
- pub fn rules<'a, 'b: 'a>(&'a self, guard: &'b SharedRwLockReadGuard) -> &'a [CssRule] {
- &self.rules.read_with(guard).0
- }
-
- /// Measure heap usage.
- #[cfg(feature = "gecko")]
- pub fn size_of(&self, guard: &SharedRwLockReadGuard, ops: &mut MallocSizeOfOps) -> usize {
- if self.rules.is_static() {
- return 0;
- }
- // Measurement of other fields may be added later.
- self.rules.unconditional_shallow_size_of(ops) +
- self.rules.read_with(guard).size_of(guard, ops)
- }
-}
-
-impl DeepCloneWithLock for StylesheetContents {
- fn deep_clone_with_lock(
- &self,
- lock: &SharedRwLock,
- guard: &SharedRwLockReadGuard,
- params: &DeepCloneParams,
- ) -> Self {
- // Make a deep clone of the rules, using the new lock.
- let rules = self
- .rules
- .read_with(guard)
- .deep_clone_with_lock(lock, guard, params);
-
- Self {
- rules: Arc::new(lock.wrap(rules)),
- quirks_mode: self.quirks_mode,
- origin: self.origin,
- url_data: RwLock::new((*self.url_data.read()).clone()),
- namespaces: RwLock::new((*self.namespaces.read()).clone()),
- source_map_url: RwLock::new((*self.source_map_url.read()).clone()),
- source_url: RwLock::new((*self.source_url.read()).clone()),
- _forbid_construction: (),
- }
- }
-}
-
-/// The structure servo uses to represent a stylesheet.
-#[derive(Debug)]
-pub struct Stylesheet {
- /// The contents of this stylesheet.
- pub contents: Arc<StylesheetContents>,
- /// The lock used for objects inside this stylesheet
- pub shared_lock: SharedRwLock,
- /// List of media associated with the Stylesheet.
- pub media: Arc<Locked<MediaList>>,
- /// Whether this stylesheet should be disabled.
- pub disabled: AtomicBool,
-}
-
-macro_rules! rule_filter {
- ($( $method: ident($variant:ident => $rule_type: ident), )+) => {
- $(
- #[allow(missing_docs)]
- fn $method<F>(&self, device: &Device, guard: &SharedRwLockReadGuard, mut f: F)
- where F: FnMut(&crate::stylesheets::$rule_type),
- {
- use crate::stylesheets::CssRule;
-
- for rule in self.effective_rules(device, guard) {
- if let CssRule::$variant(ref lock) = *rule {
- let rule = lock.read_with(guard);
- f(&rule)
- }
- }
- }
- )+
- }
-}
-
-/// A trait to represent a given stylesheet in a document.
-pub trait StylesheetInDocument: ::std::fmt::Debug {
- /// Get whether this stylesheet is enabled.
- fn enabled(&self) -> bool;
-
- /// Get the media associated with this stylesheet.
- fn media<'a>(&'a self, guard: &'a SharedRwLockReadGuard) -> Option<&'a MediaList>;
-
- /// Returns a reference to the list of rules in this stylesheet.
- fn rules<'a, 'b: 'a>(&'a self, guard: &'b SharedRwLockReadGuard) -> &'a [CssRule] {
- self.contents().rules(guard)
- }
-
- /// Returns a reference to the contents of the stylesheet.
- fn contents(&self) -> &StylesheetContents;
-
- /// Return an iterator using the condition `C`.
- #[inline]
- fn iter_rules<'a, 'b, C>(
- &'a self,
- device: &'a Device,
- guard: &'a SharedRwLockReadGuard<'b>,
- ) -> RulesIterator<'a, 'b, C>
- where
- C: NestedRuleIterationCondition,
- {
- let contents = self.contents();
- RulesIterator::new(
- device,
- contents.quirks_mode,
- guard,
- contents.rules(guard).iter(),
- )
- }
-
- /// Returns whether the style-sheet applies for the current device.
- fn is_effective_for_device(&self, device: &Device, guard: &SharedRwLockReadGuard) -> bool {
- match self.media(guard) {
- Some(medialist) => medialist.evaluate(device, self.contents().quirks_mode),
- None => true,
- }
- }
-
- /// Return an iterator over the effective rules within the style-sheet, as
- /// according to the supplied `Device`.
- #[inline]
- fn effective_rules<'a, 'b>(
- &'a self,
- device: &'a Device,
- guard: &'a SharedRwLockReadGuard<'b>,
- ) -> EffectiveRulesIterator<'a, 'b> {
- self.iter_rules::<EffectiveRules>(device, guard)
- }
-
- rule_filter! {
- effective_font_face_rules(FontFace => FontFaceRule),
- }
-}
-
-impl StylesheetInDocument for Stylesheet {
- fn media<'a>(&'a self, guard: &'a SharedRwLockReadGuard) -> Option<&'a MediaList> {
- Some(self.media.read_with(guard))
- }
-
- fn enabled(&self) -> bool {
- !self.disabled()
- }
-
- #[inline]
- fn contents(&self) -> &StylesheetContents {
- &self.contents
- }
-}
-
-/// A simple wrapper over an `Arc<Stylesheet>`, with pointer comparison, and
-/// suitable for its use in a `StylesheetSet`.
-#[derive(Clone, Debug)]
-#[cfg_attr(feature = "servo", derive(MallocSizeOf))]
-pub struct DocumentStyleSheet(
- #[cfg_attr(feature = "servo", ignore_malloc_size_of = "Arc")] pub Arc<Stylesheet>,
-);
-
-impl PartialEq for DocumentStyleSheet {
- fn eq(&self, other: &Self) -> bool {
- Arc::ptr_eq(&self.0, &other.0)
- }
-}
-
-impl StylesheetInDocument for DocumentStyleSheet {
- fn media<'a>(&'a self, guard: &'a SharedRwLockReadGuard) -> Option<&'a MediaList> {
- self.0.media(guard)
- }
-
- fn enabled(&self) -> bool {
- self.0.enabled()
- }
-
- #[inline]
- fn contents(&self) -> &StylesheetContents {
- self.0.contents()
- }
-}
-
-/// The kind of sanitization to use when parsing a stylesheet.
-#[repr(u8)]
-#[derive(Clone, Copy, Debug, PartialEq)]
-pub enum SanitizationKind {
- /// Perform no sanitization.
- None,
- /// Allow only @font-face, style rules, and @namespace.
- Standard,
- /// Allow everything but conditional rules.
- NoConditionalRules,
-}
-
-/// Whether @import rules are allowed.
-#[repr(u8)]
-#[derive(Clone, Copy, Debug, PartialEq)]
-pub enum AllowImportRules {
- /// @import rules will be parsed.
- Yes,
- /// @import rules will not be parsed.
- No,
-}
-
-impl SanitizationKind {
- fn allows(self, rule: &CssRule) -> bool {
- debug_assert_ne!(self, SanitizationKind::None);
- // NOTE(emilio): If this becomes more complex (not filtering just by
- // top-level rules), we should thread all the data through nested rules
- // and such. But this doesn't seem necessary at the moment.
- let is_standard = matches!(self, SanitizationKind::Standard);
- match *rule {
- CssRule::Document(..) |
- CssRule::Media(..) |
- CssRule::Supports(..) |
- CssRule::Import(..) |
- CssRule::Container(..) |
- // TODO(emilio): Perhaps Layer should not be always sanitized? But
- // we sanitize @media and co, so this seems safer for now.
- CssRule::LayerStatement(..) |
- CssRule::LayerBlock(..) => false,
-
- CssRule::FontFace(..) | CssRule::Namespace(..) | CssRule::Style(..) => true,
-
- CssRule::Keyframes(..) |
- CssRule::Page(..) |
- CssRule::Property(..) |
- CssRule::FontFeatureValues(..) |
- CssRule::FontPaletteValues(..) |
- CssRule::CounterStyle(..) => !is_standard,
- }
- }
-}
-
-/// A struct to hold the data relevant to style sheet sanitization.
-#[derive(Debug)]
-pub struct SanitizationData {
- kind: SanitizationKind,
- output: String,
-}
-
-impl SanitizationData {
- /// Create a new input for sanitization.
- #[inline]
- pub fn new(kind: SanitizationKind) -> Option<Self> {
- if matches!(kind, SanitizationKind::None) {
- return None;
- }
- Some(Self {
- kind,
- output: String::new(),
- })
- }
-
- /// Take the sanitized output.
- #[inline]
- pub fn take(self) -> String {
- self.output
- }
-}
-
-impl Stylesheet {
- /// Updates an empty stylesheet from a given string of text.
- pub fn update_from_str(
- existing: &Stylesheet,
- css: &str,
- url_data: UrlExtraData,
- stylesheet_loader: Option<&dyn StylesheetLoader>,
- error_reporter: Option<&dyn ParseErrorReporter>,
- line_number_offset: u32,
- allow_import_rules: AllowImportRules,
- ) {
- // FIXME: Consider adding use counters to Servo?
- let (namespaces, rules, source_map_url, source_url) = Self::parse_rules(
- css,
- &url_data,
- existing.contents.origin,
- &existing.shared_lock,
- stylesheet_loader,
- error_reporter,
- existing.contents.quirks_mode,
- line_number_offset,
- /* use_counters = */ None,
- allow_import_rules,
- /* sanitization_data = */ None,
- );
-
- *existing.contents.url_data.write() = url_data;
- *existing.contents.namespaces.write() = namespaces;
-
- // Acquire the lock *after* parsing, to minimize the exclusive section.
- let mut guard = existing.shared_lock.write();
- *existing.contents.rules.write_with(&mut guard) = CssRules(rules);
- *existing.contents.source_map_url.write() = source_map_url;
- *existing.contents.source_url.write() = source_url;
- }
-
- fn parse_rules(
- css: &str,
- url_data: &UrlExtraData,
- origin: Origin,
- shared_lock: &SharedRwLock,
- stylesheet_loader: Option<&dyn StylesheetLoader>,
- error_reporter: Option<&dyn ParseErrorReporter>,
- quirks_mode: QuirksMode,
- line_number_offset: u32,
- use_counters: Option<&UseCounters>,
- allow_import_rules: AllowImportRules,
- mut sanitization_data: Option<&mut SanitizationData>,
- ) -> (Namespaces, Vec<CssRule>, Option<String>, Option<String>) {
- let mut input = ParserInput::new_with_line_number_offset(css, line_number_offset);
- let mut input = Parser::new(&mut input);
-
- let context = ParserContext::new(
- origin,
- url_data,
- None,
- ParsingMode::DEFAULT,
- quirks_mode,
- /* namespaces = */ Default::default(),
- error_reporter,
- use_counters,
- );
-
- let mut rule_parser = TopLevelRuleParser {
- shared_lock,
- loader: stylesheet_loader,
- context,
- state: State::Start,
- dom_error: None,
- insert_rule_context: None,
- allow_import_rules,
- declaration_parser_state: Default::default(),
- rules: Vec::new(),
- };
-
- {
- let mut iter = StyleSheetParser::new(&mut input, &mut rule_parser);
- while let Some(result) = iter.next() {
- match result {
- Ok(rule_start) => {
- // TODO(emilio, nesting): sanitize nested CSS rules, probably?
- if let Some(ref mut data) = sanitization_data {
- if let Some(ref rule) = iter.parser.rules.last() {
- if !data.kind.allows(rule) {
- iter.parser.rules.pop();
- continue;
- }
- }
- let end = iter.input.position().byte_index();
- data.output.push_str(&css[rule_start.byte_index()..end]);
- }
- },
- Err((error, slice)) => {
- let location = error.location;
- let error = ContextualParseError::InvalidRule(slice, error);
- iter.parser.context.log_css_error(location, error);
- },
- }
- }
- }
-
- let source_map_url = input.current_source_map_url().map(String::from);
- let source_url = input.current_source_url().map(String::from);
- (
- rule_parser.context.namespaces.into_owned(),
- rule_parser.rules,
- source_map_url,
- source_url,
- )
- }
-
- /// Creates an empty stylesheet and parses it with a given base url, origin
- /// and media.
- ///
- /// Effectively creates a new stylesheet and forwards the hard work to
- /// `Stylesheet::update_from_str`.
- pub fn from_str(
- css: &str,
- url_data: UrlExtraData,
- origin: Origin,
- media: Arc<Locked<MediaList>>,
- shared_lock: SharedRwLock,
- stylesheet_loader: Option<&dyn StylesheetLoader>,
- error_reporter: Option<&dyn ParseErrorReporter>,
- quirks_mode: QuirksMode,
- line_number_offset: u32,
- allow_import_rules: AllowImportRules,
- ) -> Self {
- // FIXME: Consider adding use counters to Servo?
- let contents = StylesheetContents::from_str(
- css,
- url_data,
- origin,
- &shared_lock,
- stylesheet_loader,
- error_reporter,
- quirks_mode,
- line_number_offset,
- /* use_counters = */ None,
- allow_import_rules,
- /* sanitized_output = */ None,
- );
-
- Stylesheet {
- contents,
- shared_lock,
- media,
- disabled: AtomicBool::new(false),
- }
- }
-
- /// Returns whether the stylesheet has been explicitly disabled through the
- /// CSSOM.
- pub fn disabled(&self) -> bool {
- self.disabled.load(Ordering::SeqCst)
- }
-
- /// Records that the stylesheet has been explicitly disabled through the
- /// CSSOM.
- ///
- /// Returns whether the the call resulted in a change in disabled state.
- ///
- /// Disabled stylesheets remain in the document, but their rules are not
- /// added to the Stylist.
- pub fn set_disabled(&self, disabled: bool) -> bool {
- self.disabled.swap(disabled, Ordering::SeqCst) != disabled
- }
-}
-
-#[cfg(feature = "servo")]
-impl Clone for Stylesheet {
- fn clone(&self) -> Self {
- // Create a new lock for our clone.
- let lock = self.shared_lock.clone();
- let guard = self.shared_lock.read();
-
- // Make a deep clone of the media, using the new lock.
- let media = self.media.read_with(&guard).clone();
- let media = Arc::new(lock.wrap(media));
- let contents = Arc::new(self.contents.deep_clone_with_lock(
- &lock,
- &guard,
- &DeepCloneParams,
- ));
-
- Stylesheet {
- contents,
- media,
- shared_lock: lock,
- disabled: AtomicBool::new(self.disabled.load(Ordering::SeqCst)),
- }
- }
-}
diff --git a/components/style/stylesheets/supports_rule.rs b/components/style/stylesheets/supports_rule.rs
deleted file mode 100644
index 936bcdb385f..00000000000
--- a/components/style/stylesheets/supports_rule.rs
+++ /dev/null
@@ -1,448 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
-
-//! [@supports rules](https://drafts.csswg.org/css-conditional-3/#at-supports)
-
-use crate::font_face::{FontFaceSourceFormatKeyword, FontFaceSourceTechFlags};
-use crate::parser::ParserContext;
-use crate::properties::{PropertyDeclaration, PropertyId, SourcePropertyDeclaration};
-use crate::selector_parser::{SelectorImpl, SelectorParser};
-use crate::shared_lock::{DeepCloneParams, DeepCloneWithLock, Locked};
-use crate::shared_lock::{SharedRwLock, SharedRwLockReadGuard, ToCssWithGuard};
-use crate::str::CssStringWriter;
-use crate::stylesheets::{CssRuleType, CssRules};
-use cssparser::parse_important;
-use cssparser::{Delimiter, Parser, SourceLocation, Token};
-use cssparser::{ParseError as CssParseError, ParserInput};
-#[cfg(feature = "gecko")]
-use malloc_size_of::{MallocSizeOfOps, MallocUnconditionalShallowSizeOf};
-use selectors::parser::{Selector, SelectorParseErrorKind};
-use servo_arc::Arc;
-use std::ffi::{CStr, CString};
-use std::fmt::{self, Write};
-use std::str;
-use style_traits::{CssWriter, ParseError, StyleParseErrorKind, ToCss};
-
-/// An [`@supports`][supports] rule.
-///
-/// [supports]: https://drafts.csswg.org/css-conditional-3/#at-supports
-#[derive(Debug, ToShmem)]
-pub struct SupportsRule {
- /// The parsed condition
- pub condition: SupportsCondition,
- /// Child rules
- pub rules: Arc<Locked<CssRules>>,
- /// The result of evaluating the condition
- pub enabled: bool,
- /// The line and column of the rule's source code.
- pub source_location: SourceLocation,
-}
-
-impl SupportsRule {
- /// Measure heap usage.
- #[cfg(feature = "gecko")]
- pub fn size_of(&self, guard: &SharedRwLockReadGuard, ops: &mut MallocSizeOfOps) -> usize {
- // Measurement of other fields may be added later.
- self.rules.unconditional_shallow_size_of(ops) +
- self.rules.read_with(guard).size_of(guard, ops)
- }
-}
-
-impl ToCssWithGuard for SupportsRule {
- fn to_css(&self, guard: &SharedRwLockReadGuard, dest: &mut CssStringWriter) -> fmt::Result {
- dest.write_str("@supports ")?;
- self.condition.to_css(&mut CssWriter::new(dest))?;
- self.rules.read_with(guard).to_css_block(guard, dest)
- }
-}
-
-impl DeepCloneWithLock for SupportsRule {
- fn deep_clone_with_lock(
- &self,
- lock: &SharedRwLock,
- guard: &SharedRwLockReadGuard,
- params: &DeepCloneParams,
- ) -> Self {
- let rules = self.rules.read_with(guard);
- SupportsRule {
- condition: self.condition.clone(),
- rules: Arc::new(lock.wrap(rules.deep_clone_with_lock(lock, guard, params))),
- enabled: self.enabled,
- source_location: self.source_location.clone(),
- }
- }
-}
-
-/// An @supports condition
-///
-/// <https://drafts.csswg.org/css-conditional-3/#at-supports>
-#[derive(Clone, Debug, ToShmem)]
-pub enum SupportsCondition {
- /// `not (condition)`
- Not(Box<SupportsCondition>),
- /// `(condition)`
- Parenthesized(Box<SupportsCondition>),
- /// `(condition) and (condition) and (condition) ..`
- And(Vec<SupportsCondition>),
- /// `(condition) or (condition) or (condition) ..`
- Or(Vec<SupportsCondition>),
- /// `property-ident: value` (value can be any tokens)
- Declaration(Declaration),
- /// A `selector()` function.
- Selector(RawSelector),
- /// `-moz-bool-pref("pref-name")`
- /// Since we need to pass it through FFI to get the pref value,
- /// we store it as CString directly.
- MozBoolPref(CString),
- /// `font-format(<font-format>)`
- FontFormat(FontFaceSourceFormatKeyword),
- /// `font-tech(<font-tech>)`
- FontTech(FontFaceSourceTechFlags),
- /// `(any tokens)` or `func(any tokens)`
- FutureSyntax(String),
-}
-
-impl SupportsCondition {
- /// Parse a condition
- ///
- /// <https://drafts.csswg.org/css-conditional/#supports_condition>
- pub fn parse<'i, 't>(input: &mut Parser<'i, 't>) -> Result<Self, ParseError<'i>> {
- if input.try_parse(|i| i.expect_ident_matching("not")).is_ok() {
- let inner = SupportsCondition::parse_in_parens(input)?;
- return Ok(SupportsCondition::Not(Box::new(inner)));
- }
-
- let in_parens = SupportsCondition::parse_in_parens(input)?;
-
- let location = input.current_source_location();
- let (keyword, wrapper) = match input.next() {
- // End of input
- Err(..) => return Ok(in_parens),
- Ok(&Token::Ident(ref ident)) => {
- match_ignore_ascii_case! { &ident,
- "and" => ("and", SupportsCondition::And as fn(_) -> _),
- "or" => ("or", SupportsCondition::Or as fn(_) -> _),
- _ => return Err(location.new_custom_error(SelectorParseErrorKind::UnexpectedIdent(ident.clone())))
- }
- },
- Ok(t) => return Err(location.new_unexpected_token_error(t.clone())),
- };
-
- let mut conditions = Vec::with_capacity(2);
- conditions.push(in_parens);
- loop {
- conditions.push(SupportsCondition::parse_in_parens(input)?);
- if input
- .try_parse(|input| input.expect_ident_matching(keyword))
- .is_err()
- {
- // Did not find the expected keyword.
- // If we found some other token, it will be rejected by
- // `Parser::parse_entirely` somewhere up the stack.
- return Ok(wrapper(conditions));
- }
- }
- }
-
- /// Parses a functional supports condition.
- fn parse_functional<'i, 't>(
- function: &str,
- input: &mut Parser<'i, 't>,
- ) -> Result<Self, ParseError<'i>> {
- match_ignore_ascii_case! { function,
- // Although this is an internal syntax, it is not necessary
- // to check parsing context as far as we accept any
- // unexpected token as future syntax, and evaluate it to
- // false when not in chrome / ua sheet.
- // See https://drafts.csswg.org/css-conditional-3/#general_enclosed
- "-moz-bool-pref" => {
- let name = {
- let name = input.expect_string()?;
- CString::new(name.as_bytes())
- }.map_err(|_| input.new_custom_error(StyleParseErrorKind::UnspecifiedError))?;
- Ok(SupportsCondition::MozBoolPref(name))
- },
- "selector" => {
- let pos = input.position();
- consume_any_value(input)?;
- Ok(SupportsCondition::Selector(RawSelector(
- input.slice_from(pos).to_owned()
- )))
- },
- "font-format" if static_prefs::pref!("layout.css.font-tech.enabled") => {
- let kw = FontFaceSourceFormatKeyword::parse(input)?;
- Ok(SupportsCondition::FontFormat(kw))
- },
- "font-tech" if static_prefs::pref!("layout.css.font-tech.enabled") => {
- let flag = FontFaceSourceTechFlags::parse_one(input)?;
- Ok(SupportsCondition::FontTech(flag))
- },
- _ => {
- Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError))
- },
- }
- }
-
- /// Parses an `@import` condition as per
- /// https://drafts.csswg.org/css-cascade-5/#typedef-import-conditions
- pub fn parse_for_import<'i, 't>(input: &mut Parser<'i, 't>) -> Result<Self, ParseError<'i>> {
- input.expect_function_matching("supports")?;
- input.parse_nested_block(parse_condition_or_declaration)
- }
-
- /// <https://drafts.csswg.org/css-conditional-3/#supports_condition_in_parens>
- fn parse_in_parens<'i, 't>(input: &mut Parser<'i, 't>) -> Result<Self, ParseError<'i>> {
- // Whitespace is normally taken care of in `Parser::next`, but we want to not include it in
- // `pos` for the SupportsCondition::FutureSyntax cases.
- input.skip_whitespace();
- let pos = input.position();
- let location = input.current_source_location();
- match *input.next()? {
- Token::ParenthesisBlock => {
- let nested = input
- .try_parse(|input| input.parse_nested_block(parse_condition_or_declaration));
- if let Ok(nested) = nested {
- return Ok(Self::Parenthesized(Box::new(nested)));
- }
- },
- Token::Function(ref ident) => {
- let ident = ident.clone();
- let nested = input.try_parse(|input| {
- input.parse_nested_block(|input| {
- SupportsCondition::parse_functional(&ident, input)
- })
- });
- if nested.is_ok() {
- return nested;
- }
- },
- ref t => return Err(location.new_unexpected_token_error(t.clone())),
- }
- input.parse_nested_block(consume_any_value)?;
- Ok(SupportsCondition::FutureSyntax(
- input.slice_from(pos).to_owned(),
- ))
- }
-
- /// Evaluate a supports condition
- pub fn eval(&self, cx: &ParserContext) -> bool {
- match *self {
- SupportsCondition::Not(ref cond) => !cond.eval(cx),
- SupportsCondition::Parenthesized(ref cond) => cond.eval(cx),
- SupportsCondition::And(ref vec) => vec.iter().all(|c| c.eval(cx)),
- SupportsCondition::Or(ref vec) => vec.iter().any(|c| c.eval(cx)),
- SupportsCondition::Declaration(ref decl) => decl.eval(cx),
- SupportsCondition::MozBoolPref(ref name) => eval_moz_bool_pref(name, cx),
- SupportsCondition::Selector(ref selector) => selector.eval(cx),
- SupportsCondition::FontFormat(ref format) => eval_font_format(format),
- SupportsCondition::FontTech(ref tech) => eval_font_tech(tech),
- SupportsCondition::FutureSyntax(_) => false,
- }
- }
-}
-
-#[cfg(feature = "gecko")]
-fn eval_moz_bool_pref(name: &CStr, cx: &ParserContext) -> bool {
- use crate::gecko_bindings::bindings;
- if !cx.in_ua_or_chrome_sheet() {
- return false;
- }
- unsafe { bindings::Gecko_GetBoolPrefValue(name.as_ptr()) }
-}
-
-#[cfg(feature = "gecko")]
-fn eval_font_format(kw: &FontFaceSourceFormatKeyword) -> bool {
- use crate::gecko_bindings::bindings;
- unsafe { bindings::Gecko_IsFontFormatSupported(*kw) }
-}
-
-#[cfg(feature = "gecko")]
-fn eval_font_tech(flag: &FontFaceSourceTechFlags) -> bool {
- use crate::gecko_bindings::bindings;
- unsafe { bindings::Gecko_IsFontTechSupported(*flag) }
-}
-
-#[cfg(feature = "servo")]
-fn eval_moz_bool_pref(_: &CStr, _: &ParserContext) -> bool {
- false
-}
-
-#[cfg(feature = "servo")]
-fn eval_font_format(_: &FontFaceSourceFormatKeyword) -> bool {
- false
-}
-
-#[cfg(feature = "servo")]
-fn eval_font_tech(_: &FontFaceSourceTechFlags) -> bool {
- false
-}
-
-/// supports_condition | declaration
-/// <https://drafts.csswg.org/css-conditional/#dom-css-supports-conditiontext-conditiontext>
-pub fn parse_condition_or_declaration<'i, 't>(
- input: &mut Parser<'i, 't>,
-) -> Result<SupportsCondition, ParseError<'i>> {
- if let Ok(condition) = input.try_parse(SupportsCondition::parse) {
- Ok(condition)
- } else {
- Declaration::parse(input).map(SupportsCondition::Declaration)
- }
-}
-
-impl ToCss for SupportsCondition {
- fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
- where
- W: Write,
- {
- match *self {
- SupportsCondition::Not(ref cond) => {
- dest.write_str("not ")?;
- cond.to_css(dest)
- },
- SupportsCondition::Parenthesized(ref cond) => {
- dest.write_char('(')?;
- cond.to_css(dest)?;
- dest.write_char(')')
- },
- SupportsCondition::And(ref vec) => {
- let mut first = true;
- for cond in vec {
- if !first {
- dest.write_str(" and ")?;
- }
- first = false;
- cond.to_css(dest)?;
- }
- Ok(())
- },
- SupportsCondition::Or(ref vec) => {
- let mut first = true;
- for cond in vec {
- if !first {
- dest.write_str(" or ")?;
- }
- first = false;
- cond.to_css(dest)?;
- }
- Ok(())
- },
- SupportsCondition::Declaration(ref decl) => decl.to_css(dest),
- SupportsCondition::Selector(ref selector) => {
- dest.write_str("selector(")?;
- selector.to_css(dest)?;
- dest.write_char(')')
- },
- SupportsCondition::MozBoolPref(ref name) => {
- dest.write_str("-moz-bool-pref(")?;
- let name =
- str::from_utf8(name.as_bytes()).expect("Should be parsed from valid UTF-8");
- name.to_css(dest)?;
- dest.write_char(')')
- },
- SupportsCondition::FontFormat(ref kw) => {
- dest.write_str("font-format(")?;
- kw.to_css(dest)?;
- dest.write_char(')')
- },
- SupportsCondition::FontTech(ref flag) => {
- dest.write_str("font-tech(")?;
- flag.to_css(dest)?;
- dest.write_char(')')
- },
- SupportsCondition::FutureSyntax(ref s) => dest.write_str(&s),
- }
- }
-}
-
-#[derive(Clone, Debug, ToShmem)]
-/// A possibly-invalid CSS selector.
-pub struct RawSelector(pub String);
-
-impl ToCss for RawSelector {
- fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
- where
- W: Write,
- {
- dest.write_str(&self.0)
- }
-}
-
-impl RawSelector {
- /// Tries to evaluate a `selector()` function.
- pub fn eval(&self, context: &ParserContext) -> bool {
- let mut input = ParserInput::new(&self.0);
- let mut input = Parser::new(&mut input);
- input
- .parse_entirely(|input| -> Result<(), CssParseError<()>> {
- let parser = SelectorParser {
- namespaces: &context.namespaces,
- stylesheet_origin: context.stylesheet_origin,
- url_data: context.url_data,
- for_supports_rule: true,
- };
-
- Selector::<SelectorImpl>::parse(&parser, input)
- .map_err(|_| input.new_custom_error(()))?;
-
- Ok(())
- })
- .is_ok()
- }
-}
-
-#[derive(Clone, Debug, ToShmem)]
-/// A possibly-invalid property declaration
-pub struct Declaration(pub String);
-
-impl ToCss for Declaration {
- fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
- where
- W: Write,
- {
- dest.write_str(&self.0)
- }
-}
-
-/// <https://drafts.csswg.org/css-syntax-3/#typedef-any-value>
-fn consume_any_value<'i, 't>(input: &mut Parser<'i, 't>) -> Result<(), ParseError<'i>> {
- input.expect_no_error_token().map_err(|err| err.into())
-}
-
-impl Declaration {
- /// Parse a declaration
- pub fn parse<'i, 't>(input: &mut Parser<'i, 't>) -> Result<Declaration, ParseError<'i>> {
- let pos = input.position();
- input.expect_ident()?;
- input.expect_colon()?;
- consume_any_value(input)?;
- Ok(Declaration(input.slice_from(pos).to_owned()))
- }
-
- /// Determine if a declaration parses
- ///
- /// <https://drafts.csswg.org/css-conditional-3/#support-definition>
- pub fn eval(&self, context: &ParserContext) -> bool {
- debug_assert!(context.rule_types().contains(CssRuleType::Style));
-
- let mut input = ParserInput::new(&self.0);
- let mut input = Parser::new(&mut input);
- input
- .parse_entirely(|input| -> Result<(), CssParseError<()>> {
- let prop = input.expect_ident_cloned().unwrap();
- input.expect_colon().unwrap();
-
- let id =
- PropertyId::parse(&prop, context).map_err(|_| input.new_custom_error(()))?;
-
- let mut declarations = SourcePropertyDeclaration::default();
- input.parse_until_before(Delimiter::Bang, |input| {
- PropertyDeclaration::parse_into(&mut declarations, id, &context, input)
- .map_err(|_| input.new_custom_error(()))
- })?;
- let _ = input.try_parse(parse_important);
- Ok(())
- })
- .is_ok()
- }
-}
diff --git a/components/style/stylist.rs b/components/style/stylist.rs
deleted file mode 100644
index b4fd4e3a64b..00000000000
--- a/components/style/stylist.rs
+++ /dev/null
@@ -1,3290 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
-
-//! Selector matching.
-
-use crate::applicable_declarations::{
- ApplicableDeclarationBlock, ApplicableDeclarationList, CascadePriority,
-};
-use crate::context::{CascadeInputs, QuirksMode};
-use crate::dom::{TElement, TShadowRoot};
-#[cfg(feature = "gecko")]
-use crate::gecko_bindings::structs::{ServoStyleSetSizes, StyleRuleInclusion};
-use crate::invalidation::element::invalidation_map::InvalidationMap;
-use crate::invalidation::media_queries::{
- EffectiveMediaQueryResults, MediaListKey, ToMediaListKey,
-};
-use crate::invalidation::stylesheets::RuleChangeKind;
-use crate::media_queries::Device;
-use crate::properties::{self, CascadeMode, ComputedValues};
-use crate::properties::{AnimationDeclarations, PropertyDeclarationBlock};
-use crate::rule_cache::{RuleCache, RuleCacheConditions};
-use crate::rule_collector::{containing_shadow_ignoring_svg_use, RuleCollector};
-use crate::rule_tree::{CascadeLevel, RuleTree, StrongRuleNode, StyleSource};
-use crate::selector_map::{PrecomputedHashMap, PrecomputedHashSet, SelectorMap, SelectorMapEntry};
-use crate::selector_parser::{PerPseudoElementMap, PseudoElement, SelectorImpl, SnapshotMap};
-use crate::shared_lock::{Locked, SharedRwLockReadGuard, StylesheetGuards};
-use crate::stylesheet_set::{DataValidity, DocumentStylesheetSet, SheetRebuildKind};
-use crate::stylesheet_set::{DocumentStylesheetFlusher, SheetCollectionFlusher};
-use crate::stylesheets::container_rule::ContainerCondition;
-use crate::stylesheets::import_rule::ImportLayer;
-use crate::stylesheets::keyframes_rule::KeyframesAnimation;
-use crate::stylesheets::layer_rule::{LayerName, LayerOrder};
-#[cfg(feature = "gecko")]
-use crate::stylesheets::{
- CounterStyleRule, FontFaceRule, FontFeatureValuesRule, FontPaletteValuesRule, PageRule,
-};
-use crate::stylesheets::{
- CssRule, EffectiveRulesIterator, Origin, OriginSet, PageRule, PerOrigin, PerOriginIter,
-};
-use crate::stylesheets::{StyleRule, StylesheetContents, StylesheetInDocument};
-use crate::AllocErr;
-use crate::{Atom, LocalName, Namespace, ShrinkIfNeeded, WeakAtom};
-use fxhash::FxHashMap;
-use malloc_size_of::{MallocSizeOf, MallocShallowSizeOf, MallocSizeOfOps};
-#[cfg(feature = "gecko")]
-use malloc_size_of::MallocUnconditionalShallowSizeOf;
-use selectors::attr::{CaseSensitivity, NamespaceConstraint};
-use selectors::bloom::BloomFilter;
-use selectors::matching::VisitedHandlingMode;
-use selectors::matching::{matches_selector, MatchingContext, MatchingMode, NeedsSelectorFlags};
-use selectors::parser::{
- AncestorHashes, Combinator, Component, Selector, SelectorIter, SelectorList,
-};
-use selectors::visitor::{SelectorListKind, SelectorVisitor};
-use selectors::NthIndexCache;
-use servo_arc::{Arc, ArcBorrow};
-use smallbitvec::SmallBitVec;
-use smallvec::SmallVec;
-use std::borrow::Cow;
-use std::cmp::Ordering;
-use std::hash::{Hash, Hasher};
-use std::sync::Mutex;
-use std::{mem, ops};
-use style_traits::dom::{DocumentState, ElementState};
-
-/// The type of the stylesheets that the stylist contains.
-#[cfg(feature = "servo")]
-pub type StylistSheet = crate::stylesheets::DocumentStyleSheet;
-
-/// The type of the stylesheets that the stylist contains.
-#[cfg(feature = "gecko")]
-pub type StylistSheet = crate::gecko::data::GeckoStyleSheet;
-
-#[derive(Debug, Clone)]
-struct StylesheetContentsPtr(Arc<StylesheetContents>);
-
-impl PartialEq for StylesheetContentsPtr {
- #[inline]
- fn eq(&self, other: &Self) -> bool {
- Arc::ptr_eq(&self.0, &other.0)
- }
-}
-
-impl Eq for StylesheetContentsPtr {}
-
-impl Hash for StylesheetContentsPtr {
- fn hash<H: Hasher>(&self, state: &mut H) {
- let contents: &StylesheetContents = &*self.0;
- (contents as *const StylesheetContents).hash(state)
- }
-}
-
-type StyleSheetContentList = Vec<StylesheetContentsPtr>;
-
-/// A key in the cascade data cache.
-#[derive(Debug, Hash, Default, PartialEq, Eq)]
-struct CascadeDataCacheKey {
- media_query_results: Vec<MediaListKey>,
- contents: StyleSheetContentList,
-}
-
-unsafe impl Send for CascadeDataCacheKey {}
-unsafe impl Sync for CascadeDataCacheKey {}
-
-trait CascadeDataCacheEntry: Sized {
- /// Returns a reference to the cascade data.
- fn cascade_data(&self) -> &CascadeData;
- /// Rebuilds the cascade data for the new stylesheet collection. The
- /// collection is guaranteed to be dirty.
- fn rebuild<S>(
- device: &Device,
- quirks_mode: QuirksMode,
- collection: SheetCollectionFlusher<S>,
- guard: &SharedRwLockReadGuard,
- old_entry: &Self,
- ) -> Result<Arc<Self>, AllocErr>
- where
- S: StylesheetInDocument + PartialEq + 'static;
- /// Measures heap memory usage.
- #[cfg(feature = "gecko")]
- fn add_size_of(&self, ops: &mut MallocSizeOfOps, sizes: &mut ServoStyleSetSizes);
-}
-
-struct CascadeDataCache<Entry> {
- entries: FxHashMap<CascadeDataCacheKey, Arc<Entry>>,
-}
-
-impl<Entry> CascadeDataCache<Entry>
-where
- Entry: CascadeDataCacheEntry,
-{
- fn new() -> Self {
- Self {
- entries: Default::default(),
- }
- }
-
- fn len(&self) -> usize {
- self.entries.len()
- }
-
- // FIXME(emilio): This may need to be keyed on quirks-mode too, though for
- // UA sheets there aren't class / id selectors on those sheets, usually, so
- // it's probably ok... For the other cache the quirks mode shouldn't differ
- // so also should be fine.
- fn lookup<'a, S>(
- &'a mut self,
- device: &Device,
- quirks_mode: QuirksMode,
- collection: SheetCollectionFlusher<S>,
- guard: &SharedRwLockReadGuard,
- old_entry: &Entry,
- ) -> Result<Option<Arc<Entry>>, AllocErr>
- where
- S: StylesheetInDocument + PartialEq + 'static,
- {
- use std::collections::hash_map::Entry as HashMapEntry;
- debug!("StyleSheetCache::lookup({})", self.len());
-
- if !collection.dirty() {
- return Ok(None);
- }
-
- let mut key = CascadeDataCacheKey::default();
- for sheet in collection.sheets() {
- CascadeData::collect_applicable_media_query_results_into(
- device,
- sheet,
- guard,
- &mut key.media_query_results,
- &mut key.contents,
- )
- }
-
- let new_entry;
- match self.entries.entry(key) {
- HashMapEntry::Vacant(e) => {
- debug!("> Picking the slow path (not in the cache)");
- new_entry = Entry::rebuild(device, quirks_mode, collection, guard, old_entry)?;
- e.insert(new_entry.clone());
- },
- HashMapEntry::Occupied(mut e) => {
- // Avoid reusing our old entry (this can happen if we get
- // invalidated due to CSSOM mutations and our old stylesheet
- // contents were already unique, for example).
- if !std::ptr::eq(&**e.get(), old_entry) {
- if log_enabled!(log::Level::Debug) {
- debug!("cache hit for:");
- for sheet in collection.sheets() {
- debug!(" > {:?}", sheet);
- }
- }
- // The line below ensures the "committed" bit is updated
- // properly.
- collection.each(|_, _| true);
- return Ok(Some(e.get().clone()));
- }
-
- debug!("> Picking the slow path due to same entry as old");
- new_entry = Entry::rebuild(device, quirks_mode, collection, guard, old_entry)?;
- e.insert(new_entry.clone());
- },
- }
-
- Ok(Some(new_entry))
- }
-
- /// Returns all the cascade datas that are not being used (that is, that are
- /// held alive just by this cache).
- ///
- /// We return them instead of dropping in place because some of them may
- /// keep alive some other documents (like the SVG documents kept alive by
- /// URL references), and thus we don't want to drop them while locking the
- /// cache to not deadlock.
- fn take_unused(&mut self) -> SmallVec<[Arc<Entry>; 3]> {
- let mut unused = SmallVec::new();
- self.entries.retain(|_key, value| {
- // is_unique() returns false for static references, but we never
- // have static references to UserAgentCascadeDatas. If we did, it
- // may not make sense to put them in the cache in the first place.
- if !value.is_unique() {
- return true;
- }
- unused.push(value.clone());
- false
- });
- unused
- }
-
- fn take_all(&mut self) -> FxHashMap<CascadeDataCacheKey, Arc<Entry>> {
- mem::take(&mut self.entries)
- }
-
- #[cfg(feature = "gecko")]
- fn add_size_of(&self, ops: &mut MallocSizeOfOps, sizes: &mut ServoStyleSetSizes) {
- sizes.mOther += self.entries.shallow_size_of(ops);
- for (_key, arc) in self.entries.iter() {
- // These are primary Arc references that can be measured
- // unconditionally.
- sizes.mOther += arc.unconditional_shallow_size_of(ops);
- arc.add_size_of(ops, sizes);
- }
- }
-}
-
-/// Measure heap usage of UA_CASCADE_DATA_CACHE.
-#[cfg(feature = "gecko")]
-pub fn add_size_of_ua_cache(ops: &mut MallocSizeOfOps, sizes: &mut ServoStyleSetSizes) {
- UA_CASCADE_DATA_CACHE
- .lock()
- .unwrap()
- .add_size_of(ops, sizes);
-}
-
-lazy_static! {
- /// A cache of computed user-agent data, to be shared across documents.
- static ref UA_CASCADE_DATA_CACHE: Mutex<UserAgentCascadeDataCache> =
- Mutex::new(UserAgentCascadeDataCache::new());
-}
-
-impl CascadeDataCacheEntry for UserAgentCascadeData {
- fn cascade_data(&self) -> &CascadeData {
- &self.cascade_data
- }
-
- fn rebuild<S>(
- device: &Device,
- quirks_mode: QuirksMode,
- collection: SheetCollectionFlusher<S>,
- guard: &SharedRwLockReadGuard,
- _old: &Self,
- ) -> Result<Arc<Self>, AllocErr>
- where
- S: StylesheetInDocument + PartialEq + 'static,
- {
- // TODO: Maybe we should support incremental rebuilds, though they seem
- // uncommon and rebuild() doesn't deal with
- // precomputed_pseudo_element_decls for now so...
- let mut new_data = Self {
- cascade_data: CascadeData::new(),
- precomputed_pseudo_element_decls: PrecomputedPseudoElementDeclarations::default(),
- };
-
- for sheet in collection.sheets() {
- new_data.cascade_data.add_stylesheet(
- device,
- quirks_mode,
- sheet,
- guard,
- SheetRebuildKind::Full,
- Some(&mut new_data.precomputed_pseudo_element_decls),
- )?;
- }
-
- new_data.cascade_data.did_finish_rebuild();
-
- Ok(Arc::new(new_data))
- }
-
- #[cfg(feature = "gecko")]
- fn add_size_of(&self, ops: &mut MallocSizeOfOps, sizes: &mut ServoStyleSetSizes) {
- self.cascade_data.add_size_of(ops, sizes);
- sizes.mPrecomputedPseudos += self.precomputed_pseudo_element_decls.size_of(ops);
- }
-}
-
-type UserAgentCascadeDataCache = CascadeDataCache<UserAgentCascadeData>;
-
-type PrecomputedPseudoElementDeclarations = PerPseudoElementMap<Vec<ApplicableDeclarationBlock>>;
-
-#[derive(Default)]
-struct UserAgentCascadeData {
- cascade_data: CascadeData,
-
- /// Applicable declarations for a given non-eagerly cascaded pseudo-element.
- ///
- /// These are eagerly computed once, and then used to resolve the new
- /// computed values on the fly on layout.
- ///
- /// These are only filled from UA stylesheets.
- precomputed_pseudo_element_decls: PrecomputedPseudoElementDeclarations,
-}
-
-/// All the computed information for all the stylesheets that apply to the
-/// document.
-#[derive(Default)]
-#[cfg_attr(feature = "servo", derive(MallocSizeOf))]
-pub struct DocumentCascadeData {
- #[cfg_attr(
- feature = "servo",
- ignore_malloc_size_of = "Arc, owned by UserAgentCascadeDataCache"
- )]
- user_agent: Arc<UserAgentCascadeData>,
- user: CascadeData,
- author: CascadeData,
- per_origin: PerOrigin<()>,
-}
-
-/// An iterator over the cascade data of a given document.
-pub struct DocumentCascadeDataIter<'a> {
- iter: PerOriginIter<'a, ()>,
- cascade_data: &'a DocumentCascadeData,
-}
-
-impl<'a> Iterator for DocumentCascadeDataIter<'a> {
- type Item = (&'a CascadeData, Origin);
-
- fn next(&mut self) -> Option<Self::Item> {
- let (_, origin) = self.iter.next()?;
- Some((self.cascade_data.borrow_for_origin(origin), origin))
- }
-}
-
-impl DocumentCascadeData {
- /// Borrows the cascade data for a given origin.
- #[inline]
- pub fn borrow_for_origin(&self, origin: Origin) -> &CascadeData {
- match origin {
- Origin::UserAgent => &self.user_agent.cascade_data,
- Origin::Author => &self.author,
- Origin::User => &self.user,
- }
- }
-
- fn iter_origins(&self) -> DocumentCascadeDataIter {
- DocumentCascadeDataIter {
- iter: self.per_origin.iter_origins(),
- cascade_data: self,
- }
- }
-
- fn iter_origins_rev(&self) -> DocumentCascadeDataIter {
- DocumentCascadeDataIter {
- iter: self.per_origin.iter_origins_rev(),
- cascade_data: self,
- }
- }
-
- /// Rebuild the cascade data for the given document stylesheets, and
- /// optionally with a set of user agent stylesheets. Returns Err(..)
- /// to signify OOM.
- fn rebuild<'a, S>(
- &mut self,
- device: &Device,
- quirks_mode: QuirksMode,
- mut flusher: DocumentStylesheetFlusher<'a, S>,
- guards: &StylesheetGuards,
- ) -> Result<(), AllocErr>
- where
- S: StylesheetInDocument + PartialEq + 'static,
- {
- // First do UA sheets.
- {
- let origin_flusher = flusher.flush_origin(Origin::UserAgent);
- // Dirty check is just a minor optimization (no need to grab the
- // lock if nothing has changed).
- if origin_flusher.dirty() {
- let mut ua_cache = UA_CASCADE_DATA_CACHE.lock().unwrap();
- let new_data = ua_cache.lookup(
- device,
- quirks_mode,
- origin_flusher,
- guards.ua_or_user,
- &self.user_agent,
- )?;
- if let Some(new_data) = new_data {
- self.user_agent = new_data;
- }
- let _unused_entries = ua_cache.take_unused();
- // See the comments in take_unused() as for why the following
- // line.
- std::mem::drop(ua_cache);
- }
- }
-
- // Now do the user sheets.
- self.user.rebuild(
- device,
- quirks_mode,
- flusher.flush_origin(Origin::User),
- guards.ua_or_user,
- )?;
-
- // And now the author sheets.
- self.author.rebuild(
- device,
- quirks_mode,
- flusher.flush_origin(Origin::Author),
- guards.author,
- )?;
-
- Ok(())
- }
-
- /// Measures heap usage.
- #[cfg(feature = "gecko")]
- pub fn add_size_of(&self, ops: &mut MallocSizeOfOps, sizes: &mut ServoStyleSetSizes) {
- self.user.add_size_of(ops, sizes);
- self.author.add_size_of(ops, sizes);
- }
-}
-
-/// Whether author styles are enabled.
-///
-/// This is used to support Gecko.
-#[allow(missing_docs)]
-#[derive(Clone, Copy, Debug, Eq, MallocSizeOf, PartialEq)]
-pub enum AuthorStylesEnabled {
- Yes,
- No,
-}
-
-/// A wrapper over a DocumentStylesheetSet that can be `Sync`, since it's only
-/// used and exposed via mutable methods in the `Stylist`.
-#[cfg_attr(feature = "servo", derive(MallocSizeOf))]
-struct StylistStylesheetSet(DocumentStylesheetSet<StylistSheet>);
-// Read above to see why this is fine.
-unsafe impl Sync for StylistStylesheetSet {}
-
-impl StylistStylesheetSet {
- fn new() -> Self {
- StylistStylesheetSet(DocumentStylesheetSet::new())
- }
-}
-
-impl ops::Deref for StylistStylesheetSet {
- type Target = DocumentStylesheetSet<StylistSheet>;
-
- fn deref(&self) -> &Self::Target {
- &self.0
- }
-}
-
-impl ops::DerefMut for StylistStylesheetSet {
- fn deref_mut(&mut self) -> &mut Self::Target {
- &mut self.0
- }
-}
-
-/// This structure holds all the selectors and device characteristics
-/// for a given document. The selectors are converted into `Rule`s
-/// and sorted into `SelectorMap`s keyed off stylesheet origin and
-/// pseudo-element (see `CascadeData`).
-///
-/// This structure is effectively created once per pipeline, in the
-/// LayoutThread corresponding to that pipeline.
-#[cfg_attr(feature = "servo", derive(MallocSizeOf))]
-pub struct Stylist {
- /// Device that the stylist is currently evaluating against.
- ///
- /// This field deserves a bigger comment due to the different use that Gecko
- /// and Servo give to it (that we should eventually unify).
- ///
- /// With Gecko, the device is never changed. Gecko manually tracks whether
- /// the device data should be reconstructed, and "resets" the state of the
- /// device.
- ///
- /// On Servo, on the other hand, the device is a really cheap representation
- /// that is recreated each time some constraint changes and calling
- /// `set_device`.
- device: Device,
-
- /// The list of stylesheets.
- stylesheets: StylistStylesheetSet,
-
- /// A cache of CascadeDatas for AuthorStylesheetSets (i.e., shadow DOM).
- #[cfg_attr(feature = "servo", ignore_malloc_size_of = "XXX: how to handle this?")]
- author_data_cache: CascadeDataCache<CascadeData>,
-
- /// If true, the quirks-mode stylesheet is applied.
- #[cfg_attr(feature = "servo", ignore_malloc_size_of = "defined in selectors")]
- quirks_mode: QuirksMode,
-
- /// Selector maps for all of the style sheets in the stylist, after
- /// evalutaing media rules against the current device, split out per
- /// cascade level.
- cascade_data: DocumentCascadeData,
-
- /// Whether author styles are enabled.
- author_styles_enabled: AuthorStylesEnabled,
-
- /// The rule tree, that stores the results of selector matching.
- rule_tree: RuleTree,
-
- /// The total number of times the stylist has been rebuilt.
- num_rebuilds: usize,
-}
-
-/// What cascade levels to include when styling elements.
-#[derive(Clone, Copy, PartialEq)]
-pub enum RuleInclusion {
- /// Include rules for style sheets at all cascade levels. This is the
- /// normal rule inclusion mode.
- All,
- /// Only include rules from UA and user level sheets. Used to implement
- /// `getDefaultComputedStyle`.
- DefaultOnly,
-}
-
-#[cfg(feature = "gecko")]
-impl From<StyleRuleInclusion> for RuleInclusion {
- fn from(value: StyleRuleInclusion) -> Self {
- match value {
- StyleRuleInclusion::All => RuleInclusion::All,
- StyleRuleInclusion::DefaultOnly => RuleInclusion::DefaultOnly,
- }
- }
-}
-
-type AncestorSelectorList<'a> = Cow<'a, SelectorList<SelectorImpl>>;
-
-/// A struct containing state from ancestor rules like @layer / @import /
-/// @container / nesting.
-struct ContainingRuleState<'a> {
- layer_name: LayerName,
- layer_id: LayerId,
- container_condition_id: ContainerConditionId,
- ancestor_selector_lists: SmallVec<[AncestorSelectorList<'a>; 2]>,
-}
-
-impl<'a> Default for ContainingRuleState<'a> {
- fn default() -> Self {
- Self {
- layer_name: LayerName::new_empty(),
- layer_id: LayerId::root(),
- container_condition_id: ContainerConditionId::none(),
- ancestor_selector_lists: Default::default(),
- }
- }
-}
-
-struct SavedContainingRuleState {
- ancestor_selector_lists_len: usize,
- layer_name_len: usize,
- layer_id: LayerId,
- container_condition_id: ContainerConditionId,
-}
-
-impl<'a> ContainingRuleState<'a> {
- fn save(&self) -> SavedContainingRuleState {
- SavedContainingRuleState {
- ancestor_selector_lists_len: self.ancestor_selector_lists.len(),
- layer_name_len: self.layer_name.0.len(),
- layer_id: self.layer_id,
- container_condition_id: self.container_condition_id,
- }
- }
-
- fn restore(&mut self, saved: &SavedContainingRuleState) {
- debug_assert!(self.layer_name.0.len() >= saved.layer_name_len);
- debug_assert!(self.ancestor_selector_lists.len() >= saved.ancestor_selector_lists_len);
- self.ancestor_selector_lists
- .truncate(saved.ancestor_selector_lists_len);
- self.layer_name.0.truncate(saved.layer_name_len);
- self.layer_id = saved.layer_id;
- self.container_condition_id = saved.container_condition_id;
- }
-}
-
-impl Stylist {
- /// Construct a new `Stylist`, using given `Device` and `QuirksMode`.
- /// If more members are added here, think about whether they should
- /// be reset in clear().
- #[inline]
- pub fn new(device: Device, quirks_mode: QuirksMode) -> Self {
- Self {
- device,
- quirks_mode,
- stylesheets: StylistStylesheetSet::new(),
- author_data_cache: CascadeDataCache::new(),
- cascade_data: Default::default(),
- author_styles_enabled: AuthorStylesEnabled::Yes,
- rule_tree: RuleTree::new(),
- num_rebuilds: 0,
- }
- }
-
- /// Returns the document cascade data.
- #[inline]
- pub fn cascade_data(&self) -> &DocumentCascadeData {
- &self.cascade_data
- }
-
- /// Returns whether author styles are enabled or not.
- #[inline]
- pub fn author_styles_enabled(&self) -> AuthorStylesEnabled {
- self.author_styles_enabled
- }
-
- /// Iterate through all the cascade datas from the document.
- #[inline]
- pub fn iter_origins(&self) -> DocumentCascadeDataIter {
- self.cascade_data.iter_origins()
- }
-
- /// Does what the name says, to prevent author_data_cache to grow without
- /// bound.
- pub fn remove_unique_author_data_cache_entries(&mut self) {
- self.author_data_cache.take_unused();
- }
-
- /// Rebuilds (if needed) the CascadeData given a sheet collection.
- pub fn rebuild_author_data<S>(
- &mut self,
- old_data: &CascadeData,
- collection: SheetCollectionFlusher<S>,
- guard: &SharedRwLockReadGuard,
- ) -> Result<Option<Arc<CascadeData>>, AllocErr>
- where
- S: StylesheetInDocument + PartialEq + 'static,
- {
- self.author_data_cache
- .lookup(&self.device, self.quirks_mode, collection, guard, old_data)
- }
-
- /// Iterate over the extra data in origin order.
- #[inline]
- pub fn iter_extra_data_origins(&self) -> ExtraStyleDataIterator {
- ExtraStyleDataIterator(self.cascade_data.iter_origins())
- }
-
- /// Iterate over the extra data in reverse origin order.
- #[inline]
- pub fn iter_extra_data_origins_rev(&self) -> ExtraStyleDataIterator {
- ExtraStyleDataIterator(self.cascade_data.iter_origins_rev())
- }
-
- /// Returns the number of selectors.
- pub fn num_selectors(&self) -> usize {
- self.cascade_data
- .iter_origins()
- .map(|(d, _)| d.num_selectors)
- .sum()
- }
-
- /// Returns the number of declarations.
- pub fn num_declarations(&self) -> usize {
- self.cascade_data
- .iter_origins()
- .map(|(d, _)| d.num_declarations)
- .sum()
- }
-
- /// Returns the number of times the stylist has been rebuilt.
- pub fn num_rebuilds(&self) -> usize {
- self.num_rebuilds
- }
-
- /// Returns the number of revalidation_selectors.
- pub fn num_revalidation_selectors(&self) -> usize {
- self.cascade_data
- .iter_origins()
- .map(|(data, _)| data.selectors_for_cache_revalidation.len())
- .sum()
- }
-
- /// Returns the number of entries in invalidation maps.
- pub fn num_invalidations(&self) -> usize {
- self.cascade_data
- .iter_origins()
- .map(|(data, _)| data.invalidation_map.len())
- .sum()
- }
-
- /// Returns whether the given DocumentState bit is relied upon by a selector
- /// of some rule.
- pub fn has_document_state_dependency(&self, state: DocumentState) -> bool {
- self.cascade_data
- .iter_origins()
- .any(|(d, _)| d.document_state_dependencies.intersects(state))
- }
-
- /// Flush the list of stylesheets if they changed, ensuring the stylist is
- /// up-to-date.
- pub fn flush<E>(
- &mut self,
- guards: &StylesheetGuards,
- document_element: Option<E>,
- snapshots: Option<&SnapshotMap>,
- ) -> bool
- where
- E: TElement,
- {
- if !self.stylesheets.has_changed() {
- return false;
- }
-
- self.num_rebuilds += 1;
-
- let flusher = self.stylesheets.flush(document_element, snapshots);
-
- let had_invalidations = flusher.had_invalidations();
-
- self.cascade_data
- .rebuild(&self.device, self.quirks_mode, flusher, guards)
- .unwrap_or_else(|_| warn!("OOM in Stylist::flush"));
-
- had_invalidations
- }
-
- /// Insert a given stylesheet before another stylesheet in the document.
- pub fn insert_stylesheet_before(
- &mut self,
- sheet: StylistSheet,
- before_sheet: StylistSheet,
- guard: &SharedRwLockReadGuard,
- ) {
- self.stylesheets
- .insert_stylesheet_before(Some(&self.device), sheet, before_sheet, guard)
- }
-
- /// Marks a given stylesheet origin as dirty, due to, for example, changes
- /// in the declarations that affect a given rule.
- ///
- /// FIXME(emilio): Eventually it'd be nice for this to become more
- /// fine-grained.
- pub fn force_stylesheet_origins_dirty(&mut self, origins: OriginSet) {
- self.stylesheets.force_dirty(origins)
- }
-
- /// Sets whether author style is enabled or not.
- pub fn set_author_styles_enabled(&mut self, enabled: AuthorStylesEnabled) {
- self.author_styles_enabled = enabled;
- }
-
- /// Returns whether we've recorded any stylesheet change so far.
- pub fn stylesheets_have_changed(&self) -> bool {
- self.stylesheets.has_changed()
- }
-
- /// Appends a new stylesheet to the current set.
- pub fn append_stylesheet(&mut self, sheet: StylistSheet, guard: &SharedRwLockReadGuard) {
- self.stylesheets
- .append_stylesheet(Some(&self.device), sheet, guard)
- }
-
- /// Remove a given stylesheet to the current set.
- pub fn remove_stylesheet(&mut self, sheet: StylistSheet, guard: &SharedRwLockReadGuard) {
- self.stylesheets
- .remove_stylesheet(Some(&self.device), sheet, guard)
- }
-
- /// Notify of a change of a given rule.
- pub fn rule_changed(
- &mut self,
- sheet: &StylistSheet,
- rule: &CssRule,
- guard: &SharedRwLockReadGuard,
- change_kind: RuleChangeKind,
- ) {
- self.stylesheets
- .rule_changed(Some(&self.device), sheet, rule, guard, change_kind)
- }
-
- /// Appends a new stylesheet to the current set.
- #[inline]
- pub fn sheet_count(&self, origin: Origin) -> usize {
- self.stylesheets.sheet_count(origin)
- }
-
- /// Appends a new stylesheet to the current set.
- #[inline]
- pub fn sheet_at(&self, origin: Origin, index: usize) -> Option<&StylistSheet> {
- self.stylesheets.get(origin, index)
- }
-
- /// Returns whether for any of the applicable style rule data a given
- /// condition is true.
- pub fn any_applicable_rule_data<E, F>(&self, element: E, mut f: F) -> bool
- where
- E: TElement,
- F: FnMut(&CascadeData) -> bool,
- {
- if f(&self.cascade_data.user_agent.cascade_data) {
- return true;
- }
-
- let mut maybe = false;
-
- let doc_author_rules_apply =
- element.each_applicable_non_document_style_rule_data(|data, _| {
- maybe = maybe || f(&*data);
- });
-
- if maybe || f(&self.cascade_data.user) {
- return true;
- }
-
- doc_author_rules_apply && f(&self.cascade_data.author)
- }
-
- /// Computes the style for a given "precomputed" pseudo-element, taking the
- /// universal rules and applying them.
- pub fn precomputed_values_for_pseudo<E>(
- &self,
- guards: &StylesheetGuards,
- pseudo: &PseudoElement,
- parent: Option<&ComputedValues>,
- ) -> Arc<ComputedValues>
- where
- E: TElement,
- {
- debug_assert!(pseudo.is_precomputed());
-
- let rule_node = self.rule_node_for_precomputed_pseudo(guards, pseudo, vec![]);
-
- self.precomputed_values_for_pseudo_with_rule_node::<E>(guards, pseudo, parent, rule_node)
- }
-
- /// Computes the style for a given "precomputed" pseudo-element with
- /// given rule node.
- ///
- /// TODO(emilio): The type parameter could go away with a void type
- /// implementing TElement.
- pub fn precomputed_values_for_pseudo_with_rule_node<E>(
- &self,
- guards: &StylesheetGuards,
- pseudo: &PseudoElement,
- parent: Option<&ComputedValues>,
- rules: StrongRuleNode,
- ) -> Arc<ComputedValues>
- where
- E: TElement,
- {
- self.compute_pseudo_element_style_with_inputs::<E>(
- CascadeInputs {
- rules: Some(rules),
- visited_rules: None,
- flags: Default::default(),
- },
- pseudo,
- guards,
- /* originating_element_style */ None,
- parent,
- /* element */ None,
- )
- }
-
- /// Returns the rule node for a given precomputed pseudo-element.
- ///
- /// If we want to include extra declarations to this precomputed
- /// pseudo-element, we can provide a vector of ApplicableDeclarationBlocks
- /// to extra_declarations. This is useful for @page rules.
- pub fn rule_node_for_precomputed_pseudo(
- &self,
- guards: &StylesheetGuards,
- pseudo: &PseudoElement,
- mut extra_declarations: Vec<ApplicableDeclarationBlock>,
- ) -> StrongRuleNode {
- let mut declarations_with_extra;
- let declarations = match self
- .cascade_data
- .user_agent
- .precomputed_pseudo_element_decls
- .get(pseudo)
- {
- Some(declarations) => {
- if !extra_declarations.is_empty() {
- declarations_with_extra = declarations.clone();
- declarations_with_extra.append(&mut extra_declarations);
- &*declarations_with_extra
- } else {
- &**declarations
- }
- },
- None => &[],
- };
-
- self.rule_tree.insert_ordered_rules_with_important(
- declarations.into_iter().map(|a| a.clone().for_rule_tree()),
- guards,
- )
- }
-
- /// Returns the style for an anonymous box of the given type.
- ///
- /// TODO(emilio): The type parameter could go away with a void type
- /// implementing TElement.
- #[cfg(feature = "servo")]
- pub fn style_for_anonymous<E>(
- &self,
- guards: &StylesheetGuards,
- pseudo: &PseudoElement,
- parent_style: &ComputedValues,
- ) -> Arc<ComputedValues>
- where
- E: TElement,
- {
- self.precomputed_values_for_pseudo::<E>(guards, &pseudo, Some(parent_style))
- }
-
- /// Computes a pseudo-element style lazily during layout.
- ///
- /// This can only be done for a certain set of pseudo-elements, like
- /// :selection.
- ///
- /// Check the documentation on lazy pseudo-elements in
- /// docs/components/style.md
- pub fn lazily_compute_pseudo_element_style<E>(
- &self,
- guards: &StylesheetGuards,
- element: E,
- pseudo: &PseudoElement,
- rule_inclusion: RuleInclusion,
- originating_element_style: &ComputedValues,
- parent_style: &Arc<ComputedValues>,
- is_probe: bool,
- matching_fn: Option<&dyn Fn(&PseudoElement) -> bool>,
- ) -> Option<Arc<ComputedValues>>
- where
- E: TElement,
- {
- let cascade_inputs = self.lazy_pseudo_rules(
- guards,
- element,
- originating_element_style,
- parent_style,
- pseudo,
- is_probe,
- rule_inclusion,
- matching_fn,
- )?;
-
- Some(self.compute_pseudo_element_style_with_inputs(
- cascade_inputs,
- pseudo,
- guards,
- Some(originating_element_style),
- Some(parent_style),
- Some(element),
- ))
- }
-
- /// Computes a pseudo-element style lazily using the given CascadeInputs.
- /// This can be used for truly lazy pseudo-elements or to avoid redoing
- /// selector matching for eager pseudo-elements when we need to recompute
- /// their style with a new parent style.
- pub fn compute_pseudo_element_style_with_inputs<E>(
- &self,
- inputs: CascadeInputs,
- pseudo: &PseudoElement,
- guards: &StylesheetGuards,
- originating_element_style: Option<&ComputedValues>,
- parent_style: Option<&ComputedValues>,
- element: Option<E>,
- ) -> Arc<ComputedValues>
- where
- E: TElement,
- {
- // FIXME(emilio): The lack of layout_parent_style here could be
- // worrying, but we're probably dropping the display fixup for
- // pseudos other than before and after, so it's probably ok.
- //
- // (Though the flags don't indicate so!)
- //
- // It'd be fine to assert that this isn't called with a parent style
- // where display contents is in effect, but in practice this is hard to
- // do for stuff like :-moz-fieldset-content with a
- // <fieldset style="display: contents">. That is, the computed value of
- // display for the fieldset is "contents", even though it's not the used
- // value, so we don't need to adjust in a different way anyway.
- self.cascade_style_and_visited(
- element,
- Some(pseudo),
- inputs,
- guards,
- originating_element_style,
- parent_style,
- parent_style,
- parent_style,
- /* rule_cache = */ None,
- &mut RuleCacheConditions::default(),
- )
- }
-
- /// Computes a style using the given CascadeInputs. This can be used to
- /// compute a style any time we know what rules apply and just need to use
- /// the given parent styles.
- ///
- /// parent_style is the style to inherit from for properties affected by
- /// first-line ancestors.
- ///
- /// parent_style_ignoring_first_line is the style to inherit from for
- /// properties not affected by first-line ancestors.
- ///
- /// layout_parent_style is the style used for some property fixups. It's
- /// the style of the nearest ancestor with a layout box.
- pub fn cascade_style_and_visited<E>(
- &self,
- element: Option<E>,
- pseudo: Option<&PseudoElement>,
- inputs: CascadeInputs,
- guards: &StylesheetGuards,
- originating_element_style: Option<&ComputedValues>,
- parent_style: Option<&ComputedValues>,
- parent_style_ignoring_first_line: Option<&ComputedValues>,
- layout_parent_style: Option<&ComputedValues>,
- rule_cache: Option<&RuleCache>,
- rule_cache_conditions: &mut RuleCacheConditions,
- ) -> Arc<ComputedValues>
- where
- E: TElement,
- {
- debug_assert!(pseudo.is_some() || element.is_some(), "Huh?");
-
- // We need to compute visited values if we have visited rules or if our
- // parent has visited values.
- let visited_rules = match inputs.visited_rules.as_ref() {
- Some(rules) => Some(rules),
- None => {
- if parent_style.and_then(|s| s.visited_style()).is_some() {
- Some(inputs.rules.as_ref().unwrap_or(self.rule_tree.root()))
- } else {
- None
- }
- },
- };
-
- // Read the comment on `precomputed_values_for_pseudo` to see why it's
- // difficult to assert that display: contents nodes never arrive here
- // (tl;dr: It doesn't apply for replaced elements and such, but the
- // computed value is still "contents").
- //
- // FIXME(emilio): We should assert that it holds if pseudo.is_none()!
- properties::cascade::<E>(
- &self.device,
- pseudo,
- inputs.rules.as_ref().unwrap_or(self.rule_tree.root()),
- guards,
- originating_element_style,
- parent_style,
- parent_style_ignoring_first_line,
- layout_parent_style,
- visited_rules,
- inputs.flags,
- self.quirks_mode,
- rule_cache,
- rule_cache_conditions,
- element,
- )
- }
-
- /// Computes the cascade inputs for a lazily-cascaded pseudo-element.
- ///
- /// See the documentation on lazy pseudo-elements in
- /// docs/components/style.md
- fn lazy_pseudo_rules<E>(
- &self,
- guards: &StylesheetGuards,
- element: E,
- originating_element_style: &ComputedValues,
- parent_style: &Arc<ComputedValues>,
- pseudo: &PseudoElement,
- is_probe: bool,
- rule_inclusion: RuleInclusion,
- matching_fn: Option<&dyn Fn(&PseudoElement) -> bool>,
- ) -> Option<CascadeInputs>
- where
- E: TElement,
- {
- debug_assert!(pseudo.is_lazy());
-
- let mut nth_index_cache = Default::default();
- // No need to bother setting the selector flags when we're computing
- // default styles.
- let needs_selector_flags = if rule_inclusion == RuleInclusion::DefaultOnly {
- NeedsSelectorFlags::No
- } else {
- NeedsSelectorFlags::Yes
- };
-
- let mut declarations = ApplicableDeclarationList::new();
- let mut matching_context = MatchingContext::<'_, E::Impl>::new(
- MatchingMode::ForStatelessPseudoElement,
- None,
- &mut nth_index_cache,
- self.quirks_mode,
- needs_selector_flags,
- );
-
- matching_context.pseudo_element_matching_fn = matching_fn;
- matching_context.extra_data.originating_element_style = Some(originating_element_style);
-
- self.push_applicable_declarations(
- element,
- Some(&pseudo),
- None,
- None,
- /* animation_declarations = */ Default::default(),
- rule_inclusion,
- &mut declarations,
- &mut matching_context,
- );
-
- if declarations.is_empty() && is_probe {
- return None;
- }
-
- let rules = self.rule_tree.compute_rule_node(&mut declarations, guards);
-
- let mut visited_rules = None;
- if parent_style.visited_style().is_some() {
- let mut declarations = ApplicableDeclarationList::new();
- let mut nth_index_cache = Default::default();
-
- let mut matching_context = MatchingContext::<'_, E::Impl>::new_for_visited(
- MatchingMode::ForStatelessPseudoElement,
- None,
- &mut nth_index_cache,
- VisitedHandlingMode::RelevantLinkVisited,
- self.quirks_mode,
- needs_selector_flags,
- );
- matching_context.pseudo_element_matching_fn = matching_fn;
- matching_context.extra_data.originating_element_style = Some(originating_element_style);
-
- self.push_applicable_declarations(
- element,
- Some(&pseudo),
- None,
- None,
- /* animation_declarations = */ Default::default(),
- rule_inclusion,
- &mut declarations,
- &mut matching_context,
- );
- if !declarations.is_empty() {
- let rule_node = self.rule_tree.insert_ordered_rules_with_important(
- declarations.drain(..).map(|a| a.for_rule_tree()),
- guards,
- );
- if rule_node != *self.rule_tree.root() {
- visited_rules = Some(rule_node);
- }
- }
- }
-
- Some(CascadeInputs {
- rules: Some(rules),
- visited_rules,
- flags: matching_context.extra_data.cascade_input_flags,
- })
- }
-
- /// Set a given device, which may change the styles that apply to the
- /// document.
- ///
- /// Returns the sheet origins that were actually affected.
- ///
- /// This means that we may need to rebuild style data even if the
- /// stylesheets haven't changed.
- ///
- /// Also, the device that arrives here may need to take the viewport rules
- /// into account.
- pub fn set_device(&mut self, device: Device, guards: &StylesheetGuards) -> OriginSet {
- self.device = device;
- self.media_features_change_changed_style(guards, &self.device)
- }
-
- /// Returns whether, given a media feature change, any previously-applicable
- /// style has become non-applicable, or vice-versa for each origin, using
- /// `device`.
- pub fn media_features_change_changed_style(
- &self,
- guards: &StylesheetGuards,
- device: &Device,
- ) -> OriginSet {
- debug!("Stylist::media_features_change_changed_style {:?}", device);
-
- let mut origins = OriginSet::empty();
- let stylesheets = self.stylesheets.iter();
-
- for (stylesheet, origin) in stylesheets {
- if origins.contains(origin.into()) {
- continue;
- }
-
- let guard = guards.for_origin(origin);
- let origin_cascade_data = self.cascade_data.borrow_for_origin(origin);
-
- let affected_changed = !origin_cascade_data.media_feature_affected_matches(
- stylesheet,
- guard,
- device,
- self.quirks_mode,
- );
-
- if affected_changed {
- origins |= origin;
- }
- }
-
- origins
- }
-
- /// Returns the Quirks Mode of the document.
- pub fn quirks_mode(&self) -> QuirksMode {
- self.quirks_mode
- }
-
- /// Sets the quirks mode of the document.
- pub fn set_quirks_mode(&mut self, quirks_mode: QuirksMode) {
- if self.quirks_mode == quirks_mode {
- return;
- }
- self.quirks_mode = quirks_mode;
- self.force_stylesheet_origins_dirty(OriginSet::all());
- }
-
- /// Returns the applicable CSS declarations for the given element.
- pub fn push_applicable_declarations<E>(
- &self,
- element: E,
- pseudo_element: Option<&PseudoElement>,
- style_attribute: Option<ArcBorrow<Locked<PropertyDeclarationBlock>>>,
- smil_override: Option<ArcBorrow<Locked<PropertyDeclarationBlock>>>,
- animation_declarations: AnimationDeclarations,
- rule_inclusion: RuleInclusion,
- applicable_declarations: &mut ApplicableDeclarationList,
- context: &mut MatchingContext<E::Impl>,
- ) where
- E: TElement,
- {
- RuleCollector::new(
- self,
- element,
- pseudo_element,
- style_attribute,
- smil_override,
- animation_declarations,
- rule_inclusion,
- applicable_declarations,
- context,
- )
- .collect_all();
- }
-
- /// Given an id, returns whether there might be any rules for that id in any
- /// of our rule maps.
- #[inline]
- pub fn may_have_rules_for_id<E>(&self, id: &WeakAtom, element: E) -> bool
- where
- E: TElement,
- {
- // If id needs to be compared case-insensitively, the logic below
- // wouldn't work. Just conservatively assume it may have such rules.
- match self.quirks_mode().classes_and_ids_case_sensitivity() {
- CaseSensitivity::AsciiCaseInsensitive => return true,
- CaseSensitivity::CaseSensitive => {},
- }
-
- self.any_applicable_rule_data(element, |data| data.mapped_ids.contains(id))
- }
-
- /// Returns the registered `@keyframes` animation for the specified name.
- #[inline]
- pub fn get_animation<'a, E>(&'a self, name: &Atom, element: E) -> Option<&'a KeyframesAnimation>
- where
- E: TElement + 'a,
- {
- macro_rules! try_find_in {
- ($data:expr) => {
- if let Some(animation) = $data.animations.get(name) {
- return Some(animation);
- }
- };
- }
-
- // NOTE(emilio): We implement basically what Blink does for this case,
- // which is [1] as of this writing.
- //
- // See [2] for the spec discussion about what to do about this. WebKit's
- // behavior makes a bit more sense off-hand, but it's way more complex
- // to implement, and it makes value computation having to thread around
- // the cascade level, which is not great. Also, it breaks if you inherit
- // animation-name from an element in a different tree.
- //
- // See [3] for the bug to implement whatever gets resolved, and related
- // bugs for a bit more context.
- //
- // FIXME(emilio): This should probably work for pseudo-elements (i.e.,
- // use rule_hash_target().shadow_root() instead of
- // element.shadow_root()).
- //
- // [1]: https://cs.chromium.org/chromium/src/third_party/blink/renderer/
- // core/css/resolver/style_resolver.cc?l=1267&rcl=90f9f8680ebb4a87d177f3b0833372ae4e0c88d8
- // [2]: https://github.com/w3c/csswg-drafts/issues/1995
- // [3]: https://bugzil.la/1458189
- if let Some(shadow) = element.shadow_root() {
- if let Some(data) = shadow.style_data() {
- try_find_in!(data);
- }
- }
-
- // Use the same rules to look for the containing host as we do for rule
- // collection.
- if let Some(shadow) = containing_shadow_ignoring_svg_use(element) {
- if let Some(data) = shadow.style_data() {
- try_find_in!(data);
- }
- } else {
- try_find_in!(self.cascade_data.author);
- }
-
- try_find_in!(self.cascade_data.user);
- try_find_in!(self.cascade_data.user_agent.cascade_data);
-
- None
- }
-
- /// Computes the match results of a given element against the set of
- /// revalidation selectors.
- pub fn match_revalidation_selectors<E>(
- &self,
- element: E,
- bloom: Option<&BloomFilter>,
- nth_index_cache: &mut NthIndexCache,
- needs_selector_flags: NeedsSelectorFlags,
- ) -> SmallBitVec
- where
- E: TElement,
- {
- // NB: `MatchingMode` doesn't really matter, given we don't share style
- // between pseudos.
- let mut matching_context = MatchingContext::new(
- MatchingMode::Normal,
- bloom,
- nth_index_cache,
- self.quirks_mode,
- needs_selector_flags,
- );
-
- // Note that, by the time we're revalidating, we're guaranteed that the
- // candidate and the entry have the same id, classes, and local name.
- // This means we're guaranteed to get the same rulehash buckets for all
- // the lookups, which means that the bitvecs are comparable. We verify
- // this in the caller by asserting that the bitvecs are same-length.
- let mut results = SmallBitVec::new();
-
- let matches_document_rules =
- element.each_applicable_non_document_style_rule_data(|data, host| {
- matching_context.with_shadow_host(Some(host), |matching_context| {
- data.selectors_for_cache_revalidation.lookup(
- element,
- self.quirks_mode,
- |selector_and_hashes| {
- results.push(matches_selector(
- &selector_and_hashes.selector,
- selector_and_hashes.selector_offset,
- Some(&selector_and_hashes.hashes),
- &element,
- matching_context,
- ));
- true
- },
- );
- })
- });
-
- for (data, origin) in self.cascade_data.iter_origins() {
- if origin == Origin::Author && !matches_document_rules {
- continue;
- }
-
- data.selectors_for_cache_revalidation.lookup(
- element,
- self.quirks_mode,
- |selector_and_hashes| {
- results.push(matches_selector(
- &selector_and_hashes.selector,
- selector_and_hashes.selector_offset,
- Some(&selector_and_hashes.hashes),
- &element,
- &mut matching_context,
- ));
- true
- },
- );
- }
-
- results
- }
-
- /// Computes styles for a given declaration with parent_style.
- ///
- /// FIXME(emilio): the lack of pseudo / cascade flags look quite dubious,
- /// hopefully this is only used for some canvas font stuff.
- ///
- /// TODO(emilio): The type parameter can go away when
- /// https://github.com/rust-lang/rust/issues/35121 is fixed.
- pub fn compute_for_declarations<E>(
- &self,
- guards: &StylesheetGuards,
- parent_style: &ComputedValues,
- declarations: Arc<Locked<PropertyDeclarationBlock>>,
- ) -> Arc<ComputedValues>
- where
- E: TElement,
- {
- let block = declarations.read_with(guards.author);
-
- // We don't bother inserting these declarations in the rule tree, since
- // it'd be quite useless and slow.
- //
- // TODO(emilio): Now that we fixed bug 1493420, we should consider
- // reversing this as it shouldn't be slow anymore, and should avoid
- // generating two instantiations of apply_declarations.
- properties::apply_declarations::<E, _>(
- &self.device,
- /* pseudo = */ None,
- self.rule_tree.root(),
- guards,
- block.declaration_importance_iter().map(|(declaration, _)| {
- (
- declaration,
- CascadePriority::new(
- CascadeLevel::same_tree_author_normal(),
- LayerOrder::root(),
- ),
- )
- }),
- /* originating_element_style */ None,
- Some(parent_style),
- Some(parent_style),
- Some(parent_style),
- CascadeMode::Unvisited {
- visited_rules: None,
- },
- Default::default(),
- self.quirks_mode,
- /* rule_cache = */ None,
- &mut Default::default(),
- /* element = */ None,
- )
- }
-
- /// Accessor for a shared reference to the device.
- #[inline]
- pub fn device(&self) -> &Device {
- &self.device
- }
-
- /// Accessor for a mutable reference to the device.
- #[inline]
- pub fn device_mut(&mut self) -> &mut Device {
- &mut self.device
- }
-
- /// Accessor for a shared reference to the rule tree.
- #[inline]
- pub fn rule_tree(&self) -> &RuleTree {
- &self.rule_tree
- }
-
- /// Measures heap usage.
- #[cfg(feature = "gecko")]
- pub fn add_size_of(&self, ops: &mut MallocSizeOfOps, sizes: &mut ServoStyleSetSizes) {
- self.cascade_data.add_size_of(ops, sizes);
- self.author_data_cache.add_size_of(ops, sizes);
- sizes.mRuleTree += self.rule_tree.size_of(ops);
-
- // We may measure other fields in the future if DMD says it's worth it.
- }
-
- /// Shutdown the static data that this module stores.
- pub fn shutdown() {
- let _entries = UA_CASCADE_DATA_CACHE.lock().unwrap().take_all();
- }
-}
-
-/// A vector that is sorted in layer order.
-#[derive(Clone, Debug, Deref, MallocSizeOf)]
-pub struct LayerOrderedVec<T>(Vec<(T, LayerId)>);
-impl<T> Default for LayerOrderedVec<T> {
- fn default() -> Self {
- Self(Default::default())
- }
-}
-
-/// A map that is sorted in layer order.
-#[derive(Clone, Debug, Deref, MallocSizeOf)]
-pub struct LayerOrderedMap<T>(PrecomputedHashMap<Atom, SmallVec<[(T, LayerId); 1]>>);
-impl<T> Default for LayerOrderedMap<T> {
- fn default() -> Self {
- Self(Default::default())
- }
-}
-
-#[cfg(feature = "gecko")]
-impl<T: 'static> LayerOrderedVec<T> {
- fn clear(&mut self) {
- self.0.clear();
- }
- fn push(&mut self, v: T, id: LayerId) {
- self.0.push((v, id));
- }
- fn sort(&mut self, layers: &[CascadeLayer]) {
- self.0
- .sort_by_key(|&(_, ref id)| layers[id.0 as usize].order)
- }
-}
-
-impl<T: 'static> LayerOrderedMap<T> {
- fn clear(&mut self) {
- self.0.clear();
- }
- #[cfg(feature = "gecko")]
- fn try_insert(&mut self, name: Atom, v: T, id: LayerId) -> Result<(), AllocErr> {
- self.try_insert_with(name, v, id, |_, _| Ordering::Equal)
- }
- fn try_insert_with(
- &mut self,
- name: Atom,
- v: T,
- id: LayerId,
- cmp: impl Fn(&T, &T) -> Ordering,
- ) -> Result<(), AllocErr> {
- self.0.try_reserve(1)?;
- let vec = self.0.entry(name).or_default();
- if let Some(&mut (ref mut val, ref last_id)) = vec.last_mut() {
- if *last_id == id {
- if cmp(&val, &v) != Ordering::Greater {
- *val = v;
- }
- return Ok(());
- }
- }
- vec.push((v, id));
- Ok(())
- }
- #[cfg(feature = "gecko")]
- fn sort(&mut self, layers: &[CascadeLayer]) {
- self.sort_with(layers, |_, _| Ordering::Equal)
- }
- fn sort_with(&mut self, layers: &[CascadeLayer], cmp: impl Fn(&T, &T) -> Ordering) {
- for (_, v) in self.0.iter_mut() {
- v.sort_by(|&(ref v1, ref id1), &(ref v2, ref id2)| {
- let order1 = layers[id1.0 as usize].order;
- let order2 = layers[id2.0 as usize].order;
- order1.cmp(&order2).then_with(|| cmp(v1, v2))
- })
- }
- }
- /// Get an entry on the LayerOrderedMap by name.
- pub fn get(&self, name: &Atom) -> Option<&T> {
- let vec = self.0.get(name)?;
- Some(&vec.last()?.0)
- }
-}
-
-/// Wrapper to allow better tracking of memory usage by page rule lists.
-///
-/// This includes the layer ID for use with the named page table.
-#[derive(Clone, Debug, MallocSizeOf)]
-pub struct PageRuleData {
- /// Layer ID for sorting page rules after matching.
- pub layer: LayerId,
- /// Page rule
- #[ignore_malloc_size_of = "Arc, stylesheet measures as primary ref"]
- pub rule: Arc<Locked<PageRule>>,
-}
-
-/// Wrapper to allow better tracking of memory usage by page rule lists.
-///
-/// This is meant to be used by the global page rule list which are already
-/// sorted by layer ID, since all global page rules are less specific than all
-/// named page rules that match a certain page.
-#[derive(Clone, Debug, Deref, MallocSizeOf)]
-pub struct PageRuleDataNoLayer(
- #[ignore_malloc_size_of = "Arc, stylesheet measures as primary ref"] pub Arc<Locked<PageRule>>,
-);
-
-/// Stores page rules indexed by page names.
-#[derive(Clone, Debug, Default, MallocSizeOf)]
-pub struct PageRuleMap {
- /// Global, unnamed page rules.
- pub global: LayerOrderedVec<PageRuleDataNoLayer>,
- /// Named page rules
- pub named: PrecomputedHashMap<Atom, SmallVec<[PageRuleData; 1]>>,
-}
-
-#[cfg(feature = "gecko")]
-impl PageRuleMap {
- #[inline]
- fn clear(&mut self) {
- self.global.clear();
- self.named.clear();
- }
-}
-
-impl MallocShallowSizeOf for PageRuleMap {
- fn shallow_size_of(&self, ops: &mut MallocSizeOfOps) -> usize {
- self.global.size_of(ops) + self.named.shallow_size_of(ops)
- }
-}
-
-/// This struct holds data which users of Stylist may want to extract
-/// from stylesheets which can be done at the same time as updating.
-#[derive(Clone, Debug, Default)]
-#[cfg_attr(feature = "servo", derive(MallocSizeOf))]
-pub struct ExtraStyleData {
- /// A list of effective font-face rules and their origin.
- #[cfg(feature = "gecko")]
- pub font_faces: LayerOrderedVec<Arc<Locked<FontFaceRule>>>,
-
- /// A list of effective font-feature-values rules.
- #[cfg(feature = "gecko")]
- pub font_feature_values: LayerOrderedVec<Arc<FontFeatureValuesRule>>,
-
- /// A list of effective font-palette-values rules.
- #[cfg(feature = "gecko")]
- pub font_palette_values: LayerOrderedVec<Arc<FontPaletteValuesRule>>,
-
- /// A map of effective counter-style rules.
- #[cfg(feature = "gecko")]
- pub counter_styles: LayerOrderedMap<Arc<Locked<CounterStyleRule>>>,
-
- /// A map of effective page rules.
- #[cfg(feature = "gecko")]
- pub pages: PageRuleMap,
-}
-
-#[cfg(feature = "gecko")]
-impl ExtraStyleData {
- /// Add the given @font-face rule.
- fn add_font_face(&mut self, rule: &Arc<Locked<FontFaceRule>>, layer: LayerId) {
- self.font_faces.push(rule.clone(), layer);
- }
-
- /// Add the given @font-feature-values rule.
- fn add_font_feature_values(&mut self, rule: &Arc<FontFeatureValuesRule>, layer: LayerId) {
- self.font_feature_values.push(rule.clone(), layer);
- }
-
- /// Add the given @font-palette-values rule.
- fn add_font_palette_values(&mut self, rule: &Arc<FontPaletteValuesRule>, layer: LayerId) {
- self.font_palette_values.push(rule.clone(), layer);
- }
-
- /// Add the given @counter-style rule.
- fn add_counter_style(
- &mut self,
- guard: &SharedRwLockReadGuard,
- rule: &Arc<Locked<CounterStyleRule>>,
- layer: LayerId,
- ) -> Result<(), AllocErr> {
- let name = rule.read_with(guard).name().0.clone();
- self.counter_styles.try_insert(name, rule.clone(), layer)
- }
-
- /// Add the given @page rule.
- fn add_page(
- &mut self,
- guard: &SharedRwLockReadGuard,
- rule: &Arc<Locked<PageRule>>,
- layer: LayerId,
- ) -> Result<(), AllocErr> {
- let page_rule = rule.read_with(guard);
- if page_rule.selectors.0.is_empty() {
- self.pages
- .global
- .push(PageRuleDataNoLayer(rule.clone()), layer);
- } else {
- // TODO: Handle pseudo-classes
- self.pages.named.try_reserve(page_rule.selectors.0.len())?;
- for name in page_rule.selectors.as_slice() {
- let vec = self.pages.named.entry(name.0 .0.clone()).or_default();
- vec.try_reserve(1)?;
- vec.push(PageRuleData {
- layer,
- rule: rule.clone(),
- });
- }
- }
- Ok(())
- }
-
- fn sort_by_layer(&mut self, layers: &[CascadeLayer]) {
- self.font_faces.sort(layers);
- self.font_feature_values.sort(layers);
- self.font_palette_values.sort(layers);
- self.counter_styles.sort(layers);
- self.pages.global.sort(layers);
- }
-
- fn clear(&mut self) {
- #[cfg(feature = "gecko")]
- {
- self.font_faces.clear();
- self.font_feature_values.clear();
- self.font_palette_values.clear();
- self.counter_styles.clear();
- self.pages.clear();
- }
- }
-}
-
-// Don't let a prefixed keyframes animation override
-// a non-prefixed one.
-fn compare_keyframes_in_same_layer(v1: &KeyframesAnimation, v2: &KeyframesAnimation) -> Ordering {
- if v1.vendor_prefix.is_some() == v2.vendor_prefix.is_some() {
- Ordering::Equal
- } else if v2.vendor_prefix.is_some() {
- Ordering::Greater
- } else {
- Ordering::Less
- }
-}
-
-/// An iterator over the different ExtraStyleData.
-pub struct ExtraStyleDataIterator<'a>(DocumentCascadeDataIter<'a>);
-
-impl<'a> Iterator for ExtraStyleDataIterator<'a> {
- type Item = (&'a ExtraStyleData, Origin);
-
- fn next(&mut self) -> Option<Self::Item> {
- self.0.next().map(|d| (&d.0.extra_data, d.1))
- }
-}
-
-#[cfg(feature = "gecko")]
-impl MallocSizeOf for ExtraStyleData {
- /// Measure heap usage.
- fn size_of(&self, ops: &mut MallocSizeOfOps) -> usize {
- let mut n = 0;
- n += self.font_faces.shallow_size_of(ops);
- n += self.font_feature_values.shallow_size_of(ops);
- n += self.font_palette_values.shallow_size_of(ops);
- n += self.counter_styles.shallow_size_of(ops);
- n += self.pages.shallow_size_of(ops);
- n
- }
-}
-
-/// SelectorMapEntry implementation for use in our revalidation selector map.
-#[cfg_attr(feature = "gecko", derive(MallocSizeOf))]
-#[derive(Clone, Debug)]
-struct RevalidationSelectorAndHashes {
- #[cfg_attr(
- feature = "gecko",
- ignore_malloc_size_of = "CssRules have primary refs, we measure there"
- )]
- selector: Selector<SelectorImpl>,
- selector_offset: usize,
- hashes: AncestorHashes,
-}
-
-impl RevalidationSelectorAndHashes {
- fn new(selector: Selector<SelectorImpl>, hashes: AncestorHashes) -> Self {
- let selector_offset = {
- // We basically want to check whether the first combinator is a
- // pseudo-element combinator. If it is, we want to use the offset
- // one past it. Otherwise, our offset is 0.
- let mut index = 0;
- let mut iter = selector.iter();
-
- // First skip over the first ComplexSelector.
- //
- // We can't check what sort of what combinator we have until we do
- // that.
- for _ in &mut iter {
- index += 1; // Simple selector
- }
-
- match iter.next_sequence() {
- Some(Combinator::PseudoElement) => index + 1, // +1 for the combinator
- _ => 0,
- }
- };
-
- RevalidationSelectorAndHashes {
- selector,
- selector_offset,
- hashes,
- }
- }
-}
-
-impl SelectorMapEntry for RevalidationSelectorAndHashes {
- fn selector(&self) -> SelectorIter<SelectorImpl> {
- self.selector.iter_from(self.selector_offset)
- }
-}
-
-/// A selector visitor implementation that collects all the state the Stylist
-/// cares about a selector.
-struct StylistSelectorVisitor<'a> {
- /// Whether we've past the rightmost compound selector, not counting
- /// pseudo-elements.
- passed_rightmost_selector: bool,
-
- /// Whether the selector needs revalidation for the style sharing cache.
- needs_revalidation: &'a mut bool,
-
- /// Flags for which selector list-containing components the visitor is
- /// inside of, if any
- in_selector_list_of: SelectorListKind,
-
- /// The filter with all the id's getting referenced from rightmost
- /// selectors.
- mapped_ids: &'a mut PrecomputedHashSet<Atom>,
-
- /// The filter with the IDs getting referenced from the selector list of
- /// :nth-child(... of <selector list>) selectors.
- nth_of_mapped_ids: &'a mut PrecomputedHashSet<Atom>,
-
- /// The filter with the local names of attributes there are selectors for.
- attribute_dependencies: &'a mut PrecomputedHashSet<LocalName>,
-
- /// The filter with the classes getting referenced from the selector list of
- /// :nth-child(... of <selector list>) selectors.
- nth_of_class_dependencies: &'a mut PrecomputedHashSet<Atom>,
-
- /// The filter with the local names of attributes there are selectors for
- /// within the selector list of :nth-child(... of <selector list>)
- /// selectors.
- nth_of_attribute_dependencies: &'a mut PrecomputedHashSet<LocalName>,
-
- /// All the states selectors in the page reference.
- state_dependencies: &'a mut ElementState,
-
- /// All the state selectors in the page reference within the selector list
- /// of :nth-child(... of <selector list>) selectors.
- nth_of_state_dependencies: &'a mut ElementState,
-
- /// All the document states selectors in the page reference.
- document_state_dependencies: &'a mut DocumentState,
-}
-
-fn component_needs_revalidation(
- c: &Component<SelectorImpl>,
- passed_rightmost_selector: bool,
-) -> bool {
- match *c {
- Component::ID(_) => {
- // TODO(emilio): This could also check that the ID is not already in
- // the rule hash. In that case, we could avoid making this a
- // revalidation selector too.
- //
- // See https://bugzilla.mozilla.org/show_bug.cgi?id=1369611
- passed_rightmost_selector
- },
- Component::AttributeInNoNamespaceExists { .. } |
- Component::AttributeInNoNamespace { .. } |
- Component::AttributeOther(_) |
- Component::Empty |
- Component::Nth(_) |
- Component::NthOf(_) => true,
- Component::NonTSPseudoClass(ref p) => p.needs_cache_revalidation(),
- _ => false,
- }
-}
-
-impl<'a> SelectorVisitor for StylistSelectorVisitor<'a> {
- type Impl = SelectorImpl;
-
- fn visit_complex_selector(&mut self, combinator: Option<Combinator>) -> bool {
- *self.needs_revalidation =
- *self.needs_revalidation || combinator.map_or(false, |c| c.is_sibling());
-
- // NOTE(emilio): this call happens before we visit any of the simple
- // selectors in the next ComplexSelector, so we can use this to skip
- // looking at them.
- self.passed_rightmost_selector = self.passed_rightmost_selector ||
- !matches!(combinator, None | Some(Combinator::PseudoElement));
-
- true
- }
-
- fn visit_selector_list(
- &mut self,
- list_kind: SelectorListKind,
- list: &[Selector<Self::Impl>],
- ) -> bool {
- let in_selector_list_of = self.in_selector_list_of | list_kind;
- for selector in list {
- let mut nested = StylistSelectorVisitor {
- passed_rightmost_selector: false,
- needs_revalidation: &mut *self.needs_revalidation,
- in_selector_list_of,
- mapped_ids: &mut *self.mapped_ids,
- nth_of_mapped_ids: &mut *self.nth_of_mapped_ids,
- attribute_dependencies: &mut *self.attribute_dependencies,
- nth_of_class_dependencies: &mut *self.nth_of_class_dependencies,
- nth_of_attribute_dependencies: &mut *self.nth_of_attribute_dependencies,
- state_dependencies: &mut *self.state_dependencies,
- nth_of_state_dependencies: &mut *self.nth_of_state_dependencies,
- document_state_dependencies: &mut *self.document_state_dependencies,
- };
- let _ret = selector.visit(&mut nested);
- debug_assert!(_ret, "We never return false");
- }
- true
- }
-
- fn visit_attribute_selector(
- &mut self,
- _ns: &NamespaceConstraint<&Namespace>,
- name: &LocalName,
- lower_name: &LocalName,
- ) -> bool {
- if self.in_selector_list_of.in_nth_of() {
- self.nth_of_attribute_dependencies.insert(name.clone());
- if name != lower_name {
- self.nth_of_attribute_dependencies
- .insert(lower_name.clone());
- }
- }
-
- self.attribute_dependencies.insert(name.clone());
- if name != lower_name {
- self.attribute_dependencies.insert(lower_name.clone());
- }
-
- true
- }
-
- fn visit_simple_selector(&mut self, s: &Component<SelectorImpl>) -> bool {
- *self.needs_revalidation = *self.needs_revalidation ||
- component_needs_revalidation(s, self.passed_rightmost_selector);
-
- match *s {
- Component::NonTSPseudoClass(ref p) => {
- self.state_dependencies.insert(p.state_flag());
- self.document_state_dependencies
- .insert(p.document_state_flag());
-
- if self.in_selector_list_of.in_nth_of() {
- self.nth_of_state_dependencies.insert(p.state_flag());
- }
- },
- Component::ID(ref id) => {
- // We want to stop storing mapped ids as soon as we've moved off
- // the rightmost ComplexSelector that is not a pseudo-element.
- //
- // That can be detected by a visit_complex_selector call with a
- // combinator other than None and PseudoElement.
- //
- // Importantly, this call happens before we visit any of the
- // simple selectors in that ComplexSelector.
- //
- // NOTE(emilio): See the comment regarding on when this may
- // break in visit_complex_selector.
- if !self.passed_rightmost_selector {
- self.mapped_ids.insert(id.0.clone());
- }
-
- if self.in_selector_list_of.in_nth_of() {
- self.nth_of_mapped_ids.insert(id.0.clone());
- }
- },
- Component::Class(ref class) if self.in_selector_list_of.in_nth_of() => {
- self.nth_of_class_dependencies.insert(class.0.clone());
- },
- _ => {},
- }
-
- true
- }
-}
-
-/// A set of rules for element and pseudo-elements.
-#[derive(Clone, Debug, Default, MallocSizeOf)]
-struct GenericElementAndPseudoRules<Map> {
- /// Rules from stylesheets at this `CascadeData`'s origin.
- element_map: Map,
-
- /// Rules from stylesheets at this `CascadeData`'s origin that correspond
- /// to a given pseudo-element.
- ///
- /// FIXME(emilio): There are a bunch of wasted entries here in practice.
- /// Figure out a good way to do a `PerNonAnonBox` and `PerAnonBox` (for
- /// `precomputed_values_for_pseudo`) without duplicating a lot of code.
- pseudos_map: PerPseudoElementMap<Box<Map>>,
-}
-
-impl<Map: Default + MallocSizeOf> GenericElementAndPseudoRules<Map> {
- #[inline(always)]
- fn for_insertion(&mut self, pseudo_element: Option<&PseudoElement>) -> &mut Map {
- debug_assert!(
- pseudo_element.map_or(true, |pseudo| {
- !pseudo.is_precomputed() && !pseudo.is_unknown_webkit_pseudo_element()
- }),
- "Precomputed pseudos should end up in precomputed_pseudo_element_decls, \
- and unknown webkit pseudos should be discarded before getting here"
- );
-
- match pseudo_element {
- None => &mut self.element_map,
- Some(pseudo) => self
- .pseudos_map
- .get_or_insert_with(pseudo, || Box::new(Default::default())),
- }
- }
-
- #[inline]
- fn rules(&self, pseudo: Option<&PseudoElement>) -> Option<&Map> {
- match pseudo {
- Some(pseudo) => self.pseudos_map.get(pseudo).map(|p| &**p),
- None => Some(&self.element_map),
- }
- }
-
- /// Measures heap usage.
- #[cfg(feature = "gecko")]
- fn add_size_of(&self, ops: &mut MallocSizeOfOps, sizes: &mut ServoStyleSetSizes) {
- sizes.mElementAndPseudosMaps += self.element_map.size_of(ops);
-
- for elem in self.pseudos_map.iter() {
- if let Some(ref elem) = *elem {
- sizes.mElementAndPseudosMaps += <Box<_> as MallocSizeOf>::size_of(elem, ops);
- }
- }
- }
-}
-
-type ElementAndPseudoRules = GenericElementAndPseudoRules<SelectorMap<Rule>>;
-type PartMap = PrecomputedHashMap<Atom, SmallVec<[Rule; 1]>>;
-type PartElementAndPseudoRules = GenericElementAndPseudoRules<PartMap>;
-
-impl ElementAndPseudoRules {
- // TODO(emilio): Should we retain storage of these?
- fn clear(&mut self) {
- self.element_map.clear();
- self.pseudos_map.clear();
- }
-
- fn shrink_if_needed(&mut self) {
- self.element_map.shrink_if_needed();
- for pseudo in self.pseudos_map.iter_mut() {
- if let Some(ref mut pseudo) = pseudo {
- pseudo.shrink_if_needed();
- }
- }
- }
-}
-
-impl PartElementAndPseudoRules {
- // TODO(emilio): Should we retain storage of these?
- fn clear(&mut self) {
- self.element_map.clear();
- self.pseudos_map.clear();
- }
-}
-
-/// The id of a given layer, a sequentially-increasing identifier.
-#[derive(Clone, Copy, Debug, Eq, MallocSizeOf, PartialEq, PartialOrd, Ord)]
-pub struct LayerId(u16);
-
-impl LayerId {
- /// The id of the root layer.
- pub const fn root() -> Self {
- Self(0)
- }
-}
-
-#[derive(Clone, Debug, MallocSizeOf)]
-struct CascadeLayer {
- id: LayerId,
- order: LayerOrder,
- children: Vec<LayerId>,
-}
-
-impl CascadeLayer {
- const fn root() -> Self {
- Self {
- id: LayerId::root(),
- order: LayerOrder::root(),
- children: vec![],
- }
- }
-}
-
-/// The id of a given container condition, a sequentially-increasing identifier
-/// for a given style set.
-#[derive(Clone, Copy, Debug, Eq, MallocSizeOf, PartialEq, PartialOrd, Ord)]
-pub struct ContainerConditionId(u16);
-
-impl ContainerConditionId {
- /// A special id that represents no container rule all.
- pub const fn none() -> Self {
- Self(0)
- }
-}
-
-#[derive(Clone, Debug, MallocSizeOf)]
-struct ContainerConditionReference {
- parent: ContainerConditionId,
- #[ignore_malloc_size_of = "Arc"]
- condition: Option<Arc<ContainerCondition>>,
-}
-
-impl ContainerConditionReference {
- const fn none() -> Self {
- Self {
- parent: ContainerConditionId::none(),
- condition: None,
- }
- }
-}
-
-/// Data resulting from performing the CSS cascade that is specific to a given
-/// origin.
-///
-/// FIXME(emilio): Consider renaming and splitting in `CascadeData` and
-/// `InvalidationData`? That'd make `clear_cascade_data()` clearer.
-#[derive(Debug, Clone, MallocSizeOf)]
-pub struct CascadeData {
- /// The data coming from normal style rules that apply to elements at this
- /// cascade level.
- normal_rules: ElementAndPseudoRules,
-
- /// The `:host` pseudo rules that are the rightmost selector (without
- /// accounting for pseudo-elements).
- host_rules: Option<Box<ElementAndPseudoRules>>,
-
- /// The data coming from ::slotted() pseudo-element rules.
- ///
- /// We need to store them separately because an element needs to match
- /// ::slotted() pseudo-element rules in different shadow roots.
- ///
- /// In particular, we need to go through all the style data in all the
- /// containing style scopes starting from the closest assigned slot.
- slotted_rules: Option<Box<ElementAndPseudoRules>>,
-
- /// The data coming from ::part() pseudo-element rules.
- ///
- /// We need to store them separately because an element needs to match
- /// ::part() pseudo-element rules in different shadow roots.
- part_rules: Option<Box<PartElementAndPseudoRules>>,
-
- /// The invalidation map for these rules.
- invalidation_map: InvalidationMap,
-
- /// The attribute local names that appear in attribute selectors. Used
- /// to avoid taking element snapshots when an irrelevant attribute changes.
- /// (We don't bother storing the namespace, since namespaced attributes are
- /// rare.)
- attribute_dependencies: PrecomputedHashSet<LocalName>,
-
- /// The classes that appear in the selector list of
- /// :nth-child(... of <selector list>). Used to avoid restyling siblings of
- /// an element when an irrelevant class changes.
- nth_of_class_dependencies: PrecomputedHashSet<Atom>,
-
- /// The attributes that appear in the selector list of
- /// :nth-child(... of <selector list>). Used to avoid restyling siblings of
- /// an element when an irrelevant attribute changes.
- nth_of_attribute_dependencies: PrecomputedHashSet<LocalName>,
-
- /// The element state bits that are relied on by selectors. Like
- /// `attribute_dependencies`, this is used to avoid taking element snapshots
- /// when an irrelevant element state bit changes.
- state_dependencies: ElementState,
-
- /// The element state bits that are relied on by selectors that appear in
- /// the selector list of :nth-child(... of <selector list>).
- nth_of_state_dependencies: ElementState,
-
- /// The document state bits that are relied on by selectors. This is used
- /// to tell whether we need to restyle the entire document when a document
- /// state bit changes.
- document_state_dependencies: DocumentState,
-
- /// The ids that appear in the rightmost complex selector of selectors (and
- /// hence in our selector maps). Used to determine when sharing styles is
- /// safe: we disallow style sharing for elements whose id matches this
- /// filter, and hence might be in one of our selector maps.
- mapped_ids: PrecomputedHashSet<Atom>,
-
- /// The IDs that appear in the selector list of
- /// :nth-child(... of <selector list>). Used to avoid restyling siblings
- /// of an element when an irrelevant ID changes.
- nth_of_mapped_ids: PrecomputedHashSet<Atom>,
-
- /// Selectors that require explicit cache revalidation (i.e. which depend
- /// on state that is not otherwise visible to the cache, like attributes or
- /// tree-structural state like child index and pseudos).
- #[ignore_malloc_size_of = "Arc"]
- selectors_for_cache_revalidation: SelectorMap<RevalidationSelectorAndHashes>,
-
- /// A map with all the animations at this `CascadeData`'s origin, indexed
- /// by name.
- animations: LayerOrderedMap<KeyframesAnimation>,
-
- /// A map from cascade layer name to layer order.
- layer_id: FxHashMap<LayerName, LayerId>,
-
- /// The list of cascade layers, indexed by their layer id.
- layers: SmallVec<[CascadeLayer; 1]>,
-
- /// The list of container conditions, indexed by their id.
- container_conditions: SmallVec<[ContainerConditionReference; 1]>,
-
- /// Effective media query results cached from the last rebuild.
- effective_media_query_results: EffectiveMediaQueryResults,
-
- /// Extra data, like different kinds of rules, etc.
- extra_data: ExtraStyleData,
-
- /// A monotonically increasing counter to represent the order on which a
- /// style rule appears in a stylesheet, needed to sort them by source order.
- rules_source_order: u32,
-
- /// The total number of selectors.
- num_selectors: usize,
-
- /// The total number of declarations.
- num_declarations: usize,
-}
-
-impl CascadeData {
- /// Creates an empty `CascadeData`.
- pub fn new() -> Self {
- Self {
- normal_rules: ElementAndPseudoRules::default(),
- host_rules: None,
- slotted_rules: None,
- part_rules: None,
- invalidation_map: InvalidationMap::new(),
- nth_of_mapped_ids: PrecomputedHashSet::default(),
- nth_of_class_dependencies: PrecomputedHashSet::default(),
- nth_of_attribute_dependencies: PrecomputedHashSet::default(),
- nth_of_state_dependencies: ElementState::empty(),
- attribute_dependencies: PrecomputedHashSet::default(),
- state_dependencies: ElementState::empty(),
- document_state_dependencies: DocumentState::empty(),
- mapped_ids: PrecomputedHashSet::default(),
- // NOTE: We disable attribute bucketing for revalidation because we
- // rely on the buckets to match, but we don't want to just not share
- // style across elements with different attributes.
- //
- // An alternative to this would be to perform a style sharing check
- // like may_match_different_id_rules which would check that the
- // attribute buckets match on all scopes. But that seems
- // somewhat gnarly.
- selectors_for_cache_revalidation: SelectorMap::new_without_attribute_bucketing(),
- animations: Default::default(),
- layer_id: Default::default(),
- layers: smallvec::smallvec![CascadeLayer::root()],
- container_conditions: smallvec::smallvec![ContainerConditionReference::none()],
- extra_data: ExtraStyleData::default(),
- effective_media_query_results: EffectiveMediaQueryResults::new(),
- rules_source_order: 0,
- num_selectors: 0,
- num_declarations: 0,
- }
- }
-
- /// Rebuild the cascade data from a given SheetCollection, incrementally if
- /// possible.
- pub fn rebuild<'a, S>(
- &mut self,
- device: &Device,
- quirks_mode: QuirksMode,
- collection: SheetCollectionFlusher<S>,
- guard: &SharedRwLockReadGuard,
- ) -> Result<(), AllocErr>
- where
- S: StylesheetInDocument + PartialEq + 'static,
- {
- if !collection.dirty() {
- return Ok(());
- }
-
- let validity = collection.data_validity();
-
- match validity {
- DataValidity::Valid => {},
- DataValidity::CascadeInvalid => self.clear_cascade_data(),
- DataValidity::FullyInvalid => self.clear(),
- }
-
- let mut result = Ok(());
-
- collection.each(|stylesheet, rebuild_kind| {
- result = self.add_stylesheet(
- device,
- quirks_mode,
- stylesheet,
- guard,
- rebuild_kind,
- /* precomputed_pseudo_element_decls = */ None,
- );
- result.is_ok()
- });
-
- self.did_finish_rebuild();
-
- result
- }
-
- /// Returns the invalidation map.
- pub fn invalidation_map(&self) -> &InvalidationMap {
- &self.invalidation_map
- }
-
- /// Returns whether the given ElementState bit is relied upon by a selector
- /// of some rule.
- #[inline]
- pub fn has_state_dependency(&self, state: ElementState) -> bool {
- self.state_dependencies.intersects(state)
- }
-
- /// Returns whether the given ElementState bit is relied upon by a selector
- /// of some rule in the selector list of :nth-child(... of <selector list>).
- #[inline]
- pub fn has_nth_of_state_dependency(&self, state: ElementState) -> bool {
- self.nth_of_state_dependencies.intersects(state)
- }
-
- /// Returns whether the given attribute might appear in an attribute
- /// selector of some rule.
- #[inline]
- pub fn might_have_attribute_dependency(&self, local_name: &LocalName) -> bool {
- self.attribute_dependencies.contains(local_name)
- }
-
- /// Returns whether the given ID might appear in an ID selector in the
- /// selector list of :nth-child(... of <selector list>).
- #[inline]
- pub fn might_have_nth_of_id_dependency(&self, id: &Atom) -> bool {
- self.nth_of_mapped_ids.contains(id)
- }
-
- /// Returns whether the given class might appear in a class selector in the
- /// selector list of :nth-child(... of <selector list>).
- #[inline]
- pub fn might_have_nth_of_class_dependency(&self, class: &Atom) -> bool {
- self.nth_of_class_dependencies.contains(class)
- }
-
- /// Returns whether the given attribute might appear in an attribute
- /// selector in the selector list of :nth-child(... of <selector list>).
- #[inline]
- pub fn might_have_nth_of_attribute_dependency(&self, local_name: &LocalName) -> bool {
- self.nth_of_attribute_dependencies.contains(local_name)
- }
-
- /// Returns the normal rule map for a given pseudo-element.
- #[inline]
- pub fn normal_rules(&self, pseudo: Option<&PseudoElement>) -> Option<&SelectorMap<Rule>> {
- self.normal_rules.rules(pseudo)
- }
-
- /// Returns the host pseudo rule map for a given pseudo-element.
- #[inline]
- pub fn host_rules(&self, pseudo: Option<&PseudoElement>) -> Option<&SelectorMap<Rule>> {
- self.host_rules.as_ref().and_then(|d| d.rules(pseudo))
- }
-
- /// Whether there's any host rule that could match in this scope.
- pub fn any_host_rules(&self) -> bool {
- self.host_rules.is_some()
- }
-
- /// Returns the slotted rule map for a given pseudo-element.
- #[inline]
- pub fn slotted_rules(&self, pseudo: Option<&PseudoElement>) -> Option<&SelectorMap<Rule>> {
- self.slotted_rules.as_ref().and_then(|d| d.rules(pseudo))
- }
-
- /// Whether there's any ::slotted rule that could match in this scope.
- pub fn any_slotted_rule(&self) -> bool {
- self.slotted_rules.is_some()
- }
-
- /// Returns the parts rule map for a given pseudo-element.
- #[inline]
- pub fn part_rules(&self, pseudo: Option<&PseudoElement>) -> Option<&PartMap> {
- self.part_rules.as_ref().and_then(|d| d.rules(pseudo))
- }
-
- /// Whether there's any ::part rule that could match in this scope.
- pub fn any_part_rule(&self) -> bool {
- self.part_rules.is_some()
- }
-
- #[inline]
- fn layer_order_for(&self, id: LayerId) -> LayerOrder {
- self.layers[id.0 as usize].order
- }
-
- pub(crate) fn container_condition_matches<E>(
- &self,
- mut id: ContainerConditionId,
- stylist: &Stylist,
- element: E,
- context: &mut MatchingContext<E::Impl>,
- ) -> bool
- where
- E: TElement,
- {
- loop {
- let condition_ref = &self.container_conditions[id.0 as usize];
- let condition = match condition_ref.condition {
- None => return true,
- Some(ref c) => c,
- };
- let matches = condition
- .matches(
- stylist.device(),
- element,
- context.extra_data.originating_element_style,
- &mut context.extra_data.cascade_input_flags,
- )
- .to_bool(/* unknown = */ false);
- if !matches {
- return false;
- }
- id = condition_ref.parent;
- }
- }
-
- fn did_finish_rebuild(&mut self) {
- self.shrink_maps_if_needed();
- self.compute_layer_order();
- }
-
- fn shrink_maps_if_needed(&mut self) {
- self.normal_rules.shrink_if_needed();
- if let Some(ref mut host_rules) = self.host_rules {
- host_rules.shrink_if_needed();
- }
- if let Some(ref mut slotted_rules) = self.slotted_rules {
- slotted_rules.shrink_if_needed();
- }
- self.invalidation_map.shrink_if_needed();
- self.attribute_dependencies.shrink_if_needed();
- self.nth_of_attribute_dependencies.shrink_if_needed();
- self.nth_of_class_dependencies.shrink_if_needed();
- self.nth_of_mapped_ids.shrink_if_needed();
- self.mapped_ids.shrink_if_needed();
- self.layer_id.shrink_if_needed();
- self.selectors_for_cache_revalidation.shrink_if_needed();
- }
-
- fn compute_layer_order(&mut self) {
- debug_assert_ne!(
- self.layers.len(),
- 0,
- "There should be at least the root layer!"
- );
- if self.layers.len() == 1 {
- return; // Nothing to do
- }
- let (first, remaining) = self.layers.split_at_mut(1);
- let root = &mut first[0];
- let mut order = LayerOrder::first();
- compute_layer_order_for_subtree(root, remaining, &mut order);
-
- // NOTE(emilio): This is a bit trickier than it should to avoid having
- // to clone() around layer indices.
- fn compute_layer_order_for_subtree(
- parent: &mut CascadeLayer,
- remaining_layers: &mut [CascadeLayer],
- order: &mut LayerOrder,
- ) {
- for child in parent.children.iter() {
- debug_assert!(
- parent.id < *child,
- "Children are always registered after parents"
- );
- let child_index = (child.0 - parent.id.0 - 1) as usize;
- let (first, remaining) = remaining_layers.split_at_mut(child_index + 1);
- let child = &mut first[child_index];
- compute_layer_order_for_subtree(child, remaining, order);
- }
-
- if parent.id != LayerId::root() {
- parent.order = *order;
- order.inc();
- }
- }
- #[cfg(feature = "gecko")]
- {
- self.extra_data.sort_by_layer(&self.layers);
- }
- self.animations
- .sort_with(&self.layers, compare_keyframes_in_same_layer);
- }
-
- /// Collects all the applicable media query results into `results`.
- ///
- /// This duplicates part of the logic in `add_stylesheet`, which is
- /// a bit unfortunate.
- ///
- /// FIXME(emilio): With a bit of smartness in
- /// `media_feature_affected_matches`, we could convert
- /// `EffectiveMediaQueryResults` into a vector without too much effort.
- fn collect_applicable_media_query_results_into<S>(
- device: &Device,
- stylesheet: &S,
- guard: &SharedRwLockReadGuard,
- results: &mut Vec<MediaListKey>,
- contents_list: &mut StyleSheetContentList,
- ) where
- S: StylesheetInDocument + 'static,
- {
- if !stylesheet.enabled() || !stylesheet.is_effective_for_device(device, guard) {
- return;
- }
-
- debug!(" + {:?}", stylesheet);
- let contents = stylesheet.contents();
- results.push(contents.to_media_list_key());
-
- // Safety: StyleSheetContents are reference-counted with Arc.
- contents_list.push(StylesheetContentsPtr(unsafe {
- Arc::from_raw_addrefed(contents)
- }));
-
- for rule in stylesheet.effective_rules(device, guard) {
- match *rule {
- CssRule::Import(ref lock) => {
- let import_rule = lock.read_with(guard);
- debug!(" + {:?}", import_rule.stylesheet.media(guard));
- results.push(import_rule.to_media_list_key());
- },
- CssRule::Media(ref media_rule) => {
- debug!(" + {:?}", media_rule.media_queries.read_with(guard));
- results.push(media_rule.to_media_list_key());
- },
- _ => {},
- }
- }
- }
-
- fn add_rule_list<'a, S>(
- &mut self,
- rules: std::slice::Iter<'a, CssRule>,
- device: &'a Device,
- quirks_mode: QuirksMode,
- stylesheet: &S,
- guard: &'a SharedRwLockReadGuard,
- rebuild_kind: SheetRebuildKind,
- containing_rule_state: &mut ContainingRuleState<'a>,
- mut precomputed_pseudo_element_decls: Option<&mut PrecomputedPseudoElementDeclarations>,
- ) -> Result<(), AllocErr>
- where
- S: StylesheetInDocument + 'static,
- {
- for rule in rules {
- // Handle leaf rules first, as those are by far the most common
- // ones, and are always effective, so we can skip some checks.
- let mut handled = true;
- let mut selectors_for_nested_rules = None;
- match *rule {
- CssRule::Style(ref locked) => {
- let style_rule = locked.read_with(guard);
- self.num_declarations += style_rule.block.read_with(&guard).len();
-
- let has_nested_rules = style_rule.rules.is_some();
- let ancestor_selectors = containing_rule_state.ancestor_selector_lists.last();
- if has_nested_rules {
- selectors_for_nested_rules = Some(if ancestor_selectors.is_some() {
- Cow::Owned(SelectorList(Default::default()))
- } else {
- Cow::Borrowed(&style_rule.selectors)
- });
- }
-
- for selector in &style_rule.selectors.0 {
- self.num_selectors += 1;
-
- let pseudo_element = selector.pseudo_element();
- if let Some(pseudo) = pseudo_element {
- if pseudo.is_precomputed() {
- debug_assert!(selector.is_universal());
- debug_assert!(ancestor_selectors.is_none());
- debug_assert!(!has_nested_rules);
- debug_assert_eq!(stylesheet.contents().origin, Origin::UserAgent);
- debug_assert_eq!(containing_rule_state.layer_id, LayerId::root());
-
- precomputed_pseudo_element_decls
- .as_mut()
- .expect("Expected precomputed declarations for the UA level")
- .get_or_insert_with(pseudo, Vec::new)
- .push(ApplicableDeclarationBlock::new(
- StyleSource::from_rule(locked.clone()),
- self.rules_source_order,
- CascadeLevel::UANormal,
- selector.specificity(),
- LayerOrder::root(),
- ));
- continue;
- }
- if pseudo.is_unknown_webkit_pseudo_element() {
- continue;
- }
- }
-
- let selector = match ancestor_selectors {
- Some(s) => selector.replace_parent_selector(&s.0),
- None => selector.clone(),
- };
-
- let hashes = AncestorHashes::new(&selector, quirks_mode);
-
- let rule = Rule::new(
- selector,
- hashes,
- locked.clone(),
- self.rules_source_order,
- containing_rule_state.layer_id,
- containing_rule_state.container_condition_id,
- );
-
- if let Some(Cow::Owned(ref mut nested_selectors)) =
- selectors_for_nested_rules
- {
- nested_selectors.0.push(rule.selector.clone())
- }
-
- if rebuild_kind.should_rebuild_invalidation() {
- self.invalidation_map
- .note_selector(&rule.selector, quirks_mode)?;
- let mut needs_revalidation = false;
- let mut visitor = StylistSelectorVisitor {
- needs_revalidation: &mut needs_revalidation,
- passed_rightmost_selector: false,
- in_selector_list_of: SelectorListKind::default(),
- mapped_ids: &mut self.mapped_ids,
- nth_of_mapped_ids: &mut self.nth_of_mapped_ids,
- attribute_dependencies: &mut self.attribute_dependencies,
- nth_of_class_dependencies: &mut self.nth_of_class_dependencies,
- nth_of_attribute_dependencies: &mut self
- .nth_of_attribute_dependencies,
- state_dependencies: &mut self.state_dependencies,
- nth_of_state_dependencies: &mut self.nth_of_state_dependencies,
- document_state_dependencies: &mut self.document_state_dependencies,
- };
- rule.selector.visit(&mut visitor);
-
- if needs_revalidation {
- self.selectors_for_cache_revalidation.insert(
- RevalidationSelectorAndHashes::new(
- rule.selector.clone(),
- rule.hashes.clone(),
- ),
- quirks_mode,
- )?;
- }
- }
-
- // Part is special, since given it doesn't have any
- // selectors inside, it's not worth using a whole
- // SelectorMap for it.
- if let Some(parts) = rule.selector.parts() {
- // ::part() has all semantics, so we just need to
- // put any of them in the selector map.
- //
- // We choose the last one quite arbitrarily,
- // expecting it's slightly more likely to be more
- // specific.
- let map = self
- .part_rules
- .get_or_insert_with(|| Box::new(Default::default()))
- .for_insertion(pseudo_element);
- map.try_reserve(1)?;
- let vec = map.entry(parts.last().unwrap().clone().0).or_default();
- vec.try_reserve(1)?;
- vec.push(rule);
- } else {
- // NOTE(emilio): It's fine to look at :host and then at
- // ::slotted(..), since :host::slotted(..) could never
- // possibly match, as <slot> is not a valid shadow host.
- let rules = if rule
- .selector
- .is_featureless_host_selector_or_pseudo_element()
- {
- self.host_rules
- .get_or_insert_with(|| Box::new(Default::default()))
- } else if rule.selector.is_slotted() {
- self.slotted_rules
- .get_or_insert_with(|| Box::new(Default::default()))
- } else {
- &mut self.normal_rules
- }
- .for_insertion(pseudo_element);
- rules.insert(rule, quirks_mode)?;
- }
- }
- self.rules_source_order += 1;
- handled = !has_nested_rules;
- },
- CssRule::Keyframes(ref keyframes_rule) => {
- debug!("Found valid keyframes rule: {:?}", *keyframes_rule);
- let keyframes_rule = keyframes_rule.read_with(guard);
- let name = keyframes_rule.name.as_atom().clone();
- let animation = KeyframesAnimation::from_keyframes(
- &keyframes_rule.keyframes,
- keyframes_rule.vendor_prefix.clone(),
- guard,
- );
- self.animations.try_insert_with(
- name,
- animation,
- containing_rule_state.layer_id,
- compare_keyframes_in_same_layer,
- )?;
- },
- #[cfg(feature = "gecko")]
- CssRule::FontFace(ref rule) => {
- // NOTE(emilio): We don't care about container_condition_id
- // because:
- //
- // Global, name-defining at-rules such as @keyframes or
- // @font-face or @layer that are defined inside container
- // queries are not constrained by the container query
- // conditions.
- //
- // https://drafts.csswg.org/css-contain-3/#container-rule
- // (Same elsewhere)
- self.extra_data
- .add_font_face(rule, containing_rule_state.layer_id);
- },
- #[cfg(feature = "gecko")]
- CssRule::FontFeatureValues(ref rule) => {
- self.extra_data
- .add_font_feature_values(rule, containing_rule_state.layer_id);
- },
- #[cfg(feature = "gecko")]
- CssRule::FontPaletteValues(ref rule) => {
- self.extra_data
- .add_font_palette_values(rule, containing_rule_state.layer_id);
- },
- #[cfg(feature = "gecko")]
- CssRule::CounterStyle(ref rule) => {
- self.extra_data.add_counter_style(
- guard,
- rule,
- containing_rule_state.layer_id,
- )?;
- },
- #[cfg(feature = "gecko")]
- CssRule::Page(ref rule) => {
- self.extra_data
- .add_page(guard, rule, containing_rule_state.layer_id)?;
- },
- _ => {
- handled = false;
- },
- }
-
- if handled {
- // Assert that there are no children, and that the rule is
- // effective.
- if cfg!(debug_assertions) {
- let mut effective = false;
- let children = EffectiveRulesIterator::children(
- rule,
- device,
- quirks_mode,
- guard,
- &mut effective,
- );
- debug_assert!(children.is_none());
- debug_assert!(effective);
- }
- continue;
- }
-
- let mut effective = false;
- let children =
- EffectiveRulesIterator::children(rule, device, quirks_mode, guard, &mut effective);
-
- if !effective {
- continue;
- }
-
- fn maybe_register_layer(data: &mut CascadeData, layer: &LayerName) -> LayerId {
- // TODO: Measure what's more common / expensive, if
- // layer.clone() or the double hash lookup in the insert
- // case.
- if let Some(id) = data.layer_id.get(layer) {
- return *id;
- }
- let id = LayerId(data.layers.len() as u16);
-
- let parent_layer_id = if layer.layer_names().len() > 1 {
- let mut parent = layer.clone();
- parent.0.pop();
-
- *data
- .layer_id
- .get_mut(&parent)
- .expect("Parent layers should be registered before child layers")
- } else {
- LayerId::root()
- };
-
- data.layers[parent_layer_id.0 as usize].children.push(id);
- data.layers.push(CascadeLayer {
- id,
- // NOTE(emilio): Order is evaluated after rebuild in
- // compute_layer_order.
- order: LayerOrder::first(),
- children: vec![],
- });
-
- data.layer_id.insert(layer.clone(), id);
-
- id
- }
-
- fn maybe_register_layers(
- data: &mut CascadeData,
- name: Option<&LayerName>,
- containing_rule_state: &mut ContainingRuleState,
- ) {
- let anon_name;
- let name = match name {
- Some(name) => name,
- None => {
- anon_name = LayerName::new_anonymous();
- &anon_name
- },
- };
- for name in name.layer_names() {
- containing_rule_state.layer_name.0.push(name.clone());
- containing_rule_state.layer_id =
- maybe_register_layer(data, &containing_rule_state.layer_name);
- }
- debug_assert_ne!(containing_rule_state.layer_id, LayerId::root());
- }
-
- let saved_containing_rule_state = containing_rule_state.save();
- match *rule {
- CssRule::Import(ref lock) => {
- let import_rule = lock.read_with(guard);
- if rebuild_kind.should_rebuild_invalidation() {
- self.effective_media_query_results
- .saw_effective(import_rule);
- }
- match import_rule.layer {
- ImportLayer::Named(ref name) => {
- maybe_register_layers(self, Some(name), containing_rule_state)
- },
- ImportLayer::Anonymous => {
- maybe_register_layers(self, None, containing_rule_state)
- },
- ImportLayer::None => {},
- }
- },
- CssRule::Media(ref media_rule) => {
- if rebuild_kind.should_rebuild_invalidation() {
- self.effective_media_query_results
- .saw_effective(&**media_rule);
- }
- },
- CssRule::LayerBlock(ref rule) => {
- maybe_register_layers(self, rule.name.as_ref(), containing_rule_state);
- },
- CssRule::LayerStatement(ref rule) => {
- for name in &*rule.names {
- maybe_register_layers(self, Some(name), containing_rule_state);
- // Register each layer individually.
- containing_rule_state.restore(&saved_containing_rule_state);
- }
- },
- CssRule::Style(..) => {
- if let Some(s) = selectors_for_nested_rules {
- containing_rule_state.ancestor_selector_lists.push(s);
- }
- },
- CssRule::Container(ref rule) => {
- let id = ContainerConditionId(self.container_conditions.len() as u16);
- self.container_conditions.push(ContainerConditionReference {
- parent: containing_rule_state.container_condition_id,
- condition: Some(rule.condition.clone()),
- });
- containing_rule_state.container_condition_id = id;
- },
- // We don't care about any other rule.
- _ => {},
- }
-
- if let Some(children) = children {
- self.add_rule_list(
- children,
- device,
- quirks_mode,
- stylesheet,
- guard,
- rebuild_kind,
- containing_rule_state,
- precomputed_pseudo_element_decls.as_deref_mut(),
- )?;
- }
-
- containing_rule_state.restore(&saved_containing_rule_state);
- }
-
- Ok(())
- }
-
- // Returns Err(..) to signify OOM
- fn add_stylesheet<S>(
- &mut self,
- device: &Device,
- quirks_mode: QuirksMode,
- stylesheet: &S,
- guard: &SharedRwLockReadGuard,
- rebuild_kind: SheetRebuildKind,
- mut precomputed_pseudo_element_decls: Option<&mut PrecomputedPseudoElementDeclarations>,
- ) -> Result<(), AllocErr>
- where
- S: StylesheetInDocument + 'static,
- {
- if !stylesheet.enabled() || !stylesheet.is_effective_for_device(device, guard) {
- return Ok(());
- }
-
- let contents = stylesheet.contents();
-
- if rebuild_kind.should_rebuild_invalidation() {
- self.effective_media_query_results.saw_effective(contents);
- }
-
- let mut state = ContainingRuleState::default();
- self.add_rule_list(
- contents.rules(guard).iter(),
- device,
- quirks_mode,
- stylesheet,
- guard,
- rebuild_kind,
- &mut state,
- precomputed_pseudo_element_decls.as_deref_mut(),
- )?;
-
- Ok(())
- }
-
- /// Returns whether all the media-feature affected values matched before and
- /// match now in the given stylesheet.
- pub fn media_feature_affected_matches<S>(
- &self,
- stylesheet: &S,
- guard: &SharedRwLockReadGuard,
- device: &Device,
- quirks_mode: QuirksMode,
- ) -> bool
- where
- S: StylesheetInDocument + 'static,
- {
- use crate::invalidation::media_queries::PotentiallyEffectiveMediaRules;
-
- let effective_now = stylesheet.is_effective_for_device(device, guard);
-
- let effective_then = self
- .effective_media_query_results
- .was_effective(stylesheet.contents());
-
- if effective_now != effective_then {
- debug!(
- " > Stylesheet {:?} changed -> {}, {}",
- stylesheet.media(guard),
- effective_then,
- effective_now
- );
- return false;
- }
-
- if !effective_now {
- return true;
- }
-
- let mut iter = stylesheet.iter_rules::<PotentiallyEffectiveMediaRules>(device, guard);
-
- while let Some(rule) = iter.next() {
- match *rule {
- CssRule::Style(..) |
- CssRule::Namespace(..) |
- CssRule::FontFace(..) |
- CssRule::Container(..) |
- CssRule::CounterStyle(..) |
- CssRule::Supports(..) |
- CssRule::Keyframes(..) |
- CssRule::Page(..) |
- CssRule::Property(..) |
- CssRule::Document(..) |
- CssRule::LayerBlock(..) |
- CssRule::LayerStatement(..) |
- CssRule::FontPaletteValues(..) |
- CssRule::FontFeatureValues(..) => {
- // Not affected by device changes.
- continue;
- },
- CssRule::Import(ref lock) => {
- let import_rule = lock.read_with(guard);
- let effective_now = match import_rule.stylesheet.media(guard) {
- Some(m) => m.evaluate(device, quirks_mode),
- None => true,
- };
- let effective_then = self
- .effective_media_query_results
- .was_effective(import_rule);
- if effective_now != effective_then {
- debug!(
- " > @import rule {:?} changed {} -> {}",
- import_rule.stylesheet.media(guard),
- effective_then,
- effective_now
- );
- return false;
- }
-
- if !effective_now {
- iter.skip_children();
- }
- },
- CssRule::Media(ref media_rule) => {
- let mq = media_rule.media_queries.read_with(guard);
- let effective_now = mq.evaluate(device, quirks_mode);
- let effective_then = self
- .effective_media_query_results
- .was_effective(&**media_rule);
-
- if effective_now != effective_then {
- debug!(
- " > @media rule {:?} changed {} -> {}",
- mq, effective_then, effective_now
- );
- return false;
- }
-
- if !effective_now {
- iter.skip_children();
- }
- },
- }
- }
-
- true
- }
-
- /// Clears the cascade data, but not the invalidation data.
- fn clear_cascade_data(&mut self) {
- self.normal_rules.clear();
- if let Some(ref mut slotted_rules) = self.slotted_rules {
- slotted_rules.clear();
- }
- if let Some(ref mut part_rules) = self.part_rules {
- part_rules.clear();
- }
- if let Some(ref mut host_rules) = self.host_rules {
- host_rules.clear();
- }
- self.animations.clear();
- self.layer_id.clear();
- self.layers.clear();
- self.layers.push(CascadeLayer::root());
- self.container_conditions.clear();
- self.container_conditions
- .push(ContainerConditionReference::none());
- #[cfg(feature = "gecko")]
- {
- self.extra_data.clear();
- }
- self.rules_source_order = 0;
- self.num_selectors = 0;
- self.num_declarations = 0;
- }
-
- fn clear(&mut self) {
- self.clear_cascade_data();
- self.invalidation_map.clear();
- self.attribute_dependencies.clear();
- self.nth_of_attribute_dependencies.clear();
- self.nth_of_class_dependencies.clear();
- self.state_dependencies = ElementState::empty();
- self.nth_of_state_dependencies = ElementState::empty();
- self.document_state_dependencies = DocumentState::empty();
- self.mapped_ids.clear();
- self.nth_of_mapped_ids.clear();
- self.selectors_for_cache_revalidation.clear();
- self.effective_media_query_results.clear();
- }
-}
-
-impl CascadeDataCacheEntry for CascadeData {
- fn cascade_data(&self) -> &CascadeData {
- self
- }
-
- fn rebuild<S>(
- device: &Device,
- quirks_mode: QuirksMode,
- collection: SheetCollectionFlusher<S>,
- guard: &SharedRwLockReadGuard,
- old: &Self,
- ) -> Result<Arc<Self>, AllocErr>
- where
- S: StylesheetInDocument + PartialEq + 'static,
- {
- debug_assert!(collection.dirty(), "We surely need to do something?");
- // If we're doing a full rebuild anyways, don't bother cloning the data.
- let mut updatable_entry = match collection.data_validity() {
- DataValidity::Valid | DataValidity::CascadeInvalid => old.clone(),
- DataValidity::FullyInvalid => Self::new(),
- };
- updatable_entry.rebuild(device, quirks_mode, collection, guard)?;
- Ok(Arc::new(updatable_entry))
- }
-
- #[cfg(feature = "gecko")]
- fn add_size_of(&self, ops: &mut MallocSizeOfOps, sizes: &mut ServoStyleSetSizes) {
- self.normal_rules.add_size_of(ops, sizes);
- if let Some(ref slotted_rules) = self.slotted_rules {
- slotted_rules.add_size_of(ops, sizes);
- }
- if let Some(ref part_rules) = self.part_rules {
- part_rules.add_size_of(ops, sizes);
- }
- if let Some(ref host_rules) = self.host_rules {
- host_rules.add_size_of(ops, sizes);
- }
- sizes.mInvalidationMap += self.invalidation_map.size_of(ops);
- sizes.mRevalidationSelectors += self.selectors_for_cache_revalidation.size_of(ops);
- sizes.mOther += self.animations.size_of(ops);
- sizes.mOther += self.effective_media_query_results.size_of(ops);
- sizes.mOther += self.extra_data.size_of(ops);
- }
-}
-
-impl Default for CascadeData {
- fn default() -> Self {
- CascadeData::new()
- }
-}
-
-/// A rule, that wraps a style rule, but represents a single selector of the
-/// rule.
-#[derive(Clone, Debug, MallocSizeOf)]
-pub struct Rule {
- /// The selector this struct represents. We store this and the
- /// any_{important,normal} booleans inline in the Rule to avoid
- /// pointer-chasing when gathering applicable declarations, which
- /// can ruin performance when there are a lot of rules.
- #[ignore_malloc_size_of = "CssRules have primary refs, we measure there"]
- pub selector: Selector<SelectorImpl>,
-
- /// The ancestor hashes associated with the selector.
- pub hashes: AncestorHashes,
-
- /// The source order this style rule appears in. Note that we only use
- /// three bytes to store this value in ApplicableDeclarationsBlock, so
- /// we could repurpose that storage here if we needed to.
- pub source_order: u32,
-
- /// The current layer id of this style rule.
- pub layer_id: LayerId,
-
- /// The current @container rule id.
- pub container_condition_id: ContainerConditionId,
-
- /// The actual style rule.
- #[cfg_attr(
- feature = "gecko",
- ignore_malloc_size_of = "Secondary ref. Primary ref is in StyleRule under Stylesheet."
- )]
- #[cfg_attr(feature = "servo", ignore_malloc_size_of = "Arc")]
- pub style_rule: Arc<Locked<StyleRule>>,
-}
-
-impl SelectorMapEntry for Rule {
- fn selector(&self) -> SelectorIter<SelectorImpl> {
- self.selector.iter()
- }
-}
-
-impl Rule {
- /// Returns the specificity of the rule.
- pub fn specificity(&self) -> u32 {
- self.selector.specificity()
- }
-
- /// Turns this rule into an `ApplicableDeclarationBlock` for the given
- /// cascade level.
- pub fn to_applicable_declaration_block(
- &self,
- level: CascadeLevel,
- cascade_data: &CascadeData,
- ) -> ApplicableDeclarationBlock {
- let source = StyleSource::from_rule(self.style_rule.clone());
- ApplicableDeclarationBlock::new(
- source,
- self.source_order,
- level,
- self.specificity(),
- cascade_data.layer_order_for(self.layer_id),
- )
- }
-
- /// Creates a new Rule.
- pub fn new(
- selector: Selector<SelectorImpl>,
- hashes: AncestorHashes,
- style_rule: Arc<Locked<StyleRule>>,
- source_order: u32,
- layer_id: LayerId,
- container_condition_id: ContainerConditionId,
- ) -> Self {
- Rule {
- selector,
- hashes,
- style_rule,
- source_order,
- layer_id,
- container_condition_id,
- }
- }
-}
-
-// The size of this is critical to performance on the bloom-basic
-// microbenchmark.
-// When iterating over a large Rule array, we want to be able to fast-reject
-// selectors (with the inline hashes) with as few cache misses as possible.
-size_of_test!(Rule, 40);
-
-/// A function to be able to test the revalidation stuff.
-pub fn needs_revalidation_for_testing(s: &Selector<SelectorImpl>) -> bool {
- let mut needs_revalidation = false;
- let mut mapped_ids = Default::default();
- let mut nth_of_mapped_ids = Default::default();
- let mut attribute_dependencies = Default::default();
- let mut nth_of_class_dependencies = Default::default();
- let mut nth_of_attribute_dependencies = Default::default();
- let mut state_dependencies = ElementState::empty();
- let mut nth_of_state_dependencies = ElementState::empty();
- let mut document_state_dependencies = DocumentState::empty();
- let mut visitor = StylistSelectorVisitor {
- passed_rightmost_selector: false,
- needs_revalidation: &mut needs_revalidation,
- in_selector_list_of: SelectorListKind::default(),
- mapped_ids: &mut mapped_ids,
- nth_of_mapped_ids: &mut nth_of_mapped_ids,
- attribute_dependencies: &mut attribute_dependencies,
- nth_of_class_dependencies: &mut nth_of_class_dependencies,
- nth_of_attribute_dependencies: &mut nth_of_attribute_dependencies,
- state_dependencies: &mut state_dependencies,
- nth_of_state_dependencies: &mut nth_of_state_dependencies,
- document_state_dependencies: &mut document_state_dependencies,
- };
- s.visit(&mut visitor);
- needs_revalidation
-}
diff --git a/components/style/thread_state.rs b/components/style/thread_state.rs
deleted file mode 100644
index 2a39feef487..00000000000
--- a/components/style/thread_state.rs
+++ /dev/null
@@ -1,97 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
-
-//! Supports dynamic assertions in about what sort of thread is running and
-//! what state it's in.
-
-#![deny(missing_docs)]
-
-use std::cell::RefCell;
-
-bitflags! {
- /// A thread state flag, used for multiple assertions.
- pub struct ThreadState: u32 {
- /// Whether we're in a script thread.
- const SCRIPT = 0x01;
- /// Whether we're in a layout thread.
- const LAYOUT = 0x02;
-
- /// Whether we're in a script worker thread (actual web workers), or in
- /// a layout worker thread.
- const IN_WORKER = 0x0100;
-
- /// Whether the current thread is going through a GC.
- const IN_GC = 0x0200;
- }
-}
-
-macro_rules! thread_types ( ( $( $fun:ident = $flag:path ; )* ) => (
- impl ThreadState {
- /// Whether the current thread is a worker thread.
- pub fn is_worker(self) -> bool {
- self.contains(ThreadState::IN_WORKER)
- }
-
- $(
- #[allow(missing_docs)]
- pub fn $fun(self) -> bool {
- self.contains($flag)
- }
- )*
- }
-));
-
-thread_types! {
- is_script = ThreadState::SCRIPT;
- is_layout = ThreadState::LAYOUT;
-}
-
-thread_local!(static STATE: RefCell<Option<ThreadState>> = RefCell::new(None));
-
-/// Initializes the current thread state.
-pub fn initialize(x: ThreadState) {
- STATE.with(|ref k| {
- if let Some(ref s) = *k.borrow() {
- if x != *s {
- panic!("Thread state already initialized as {:?}", s);
- }
- }
- *k.borrow_mut() = Some(x);
- });
-}
-
-/// Initializes the current thread as a layout worker thread.
-pub fn initialize_layout_worker_thread() {
- initialize(ThreadState::LAYOUT | ThreadState::IN_WORKER);
-}
-
-/// Gets the current thread state.
-pub fn get() -> ThreadState {
- let state = STATE.with(|ref k| {
- match *k.borrow() {
- None => ThreadState::empty(), // Unknown thread.
- Some(s) => s,
- }
- });
-
- state
-}
-
-/// Enters into a given temporary state. Panics if re-entring.
-pub fn enter(x: ThreadState) {
- let state = get();
- debug_assert!(!state.intersects(x));
- STATE.with(|ref k| {
- *k.borrow_mut() = Some(state | x);
- })
-}
-
-/// Exits a given temporary state.
-pub fn exit(x: ThreadState) {
- let state = get();
- debug_assert!(state.contains(x));
- STATE.with(|ref k| {
- *k.borrow_mut() = Some(state & !x);
- })
-}
diff --git a/components/style/traversal.rs b/components/style/traversal.rs
deleted file mode 100644
index 19cc45723cf..00000000000
--- a/components/style/traversal.rs
+++ /dev/null
@@ -1,841 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
-
-//! Traversing the DOM tree; the bloom filter.
-
-use crate::context::{ElementCascadeInputs, SharedStyleContext, StyleContext};
-use crate::data::{ElementData, ElementStyles, RestyleKind};
-use crate::dom::{NodeInfo, OpaqueNode, TElement, TNode};
-use crate::invalidation::element::restyle_hints::RestyleHint;
-use crate::matching::{ChildRestyleRequirement, MatchMethods};
-use crate::selector_parser::PseudoElement;
-use crate::sharing::StyleSharingTarget;
-use crate::style_resolver::{PseudoElementResolution, StyleResolverForElement};
-use crate::stylist::RuleInclusion;
-use crate::traversal_flags::TraversalFlags;
-use selectors::NthIndexCache;
-use smallvec::SmallVec;
-use std::collections::HashMap;
-
-/// A cache from element reference to known-valid computed style.
-pub type UndisplayedStyleCache =
- HashMap<selectors::OpaqueElement, servo_arc::Arc<crate::properties::ComputedValues>>;
-
-/// A per-traversal-level chunk of data. This is sent down by the traversal, and
-/// currently only holds the dom depth for the bloom filter.
-///
-/// NB: Keep this as small as possible, please!
-#[derive(Clone, Copy, Debug)]
-pub struct PerLevelTraversalData {
- /// The current dom depth.
- ///
- /// This is kept with cooperation from the traversal code and the bloom
- /// filter.
- pub current_dom_depth: usize,
-}
-
-/// We use this structure, rather than just returning a boolean from pre_traverse,
-/// to enfore that callers process root invalidations before starting the traversal.
-pub struct PreTraverseToken<E: TElement>(Option<E>);
-impl<E: TElement> PreTraverseToken<E> {
- /// Whether we should traverse children.
- pub fn should_traverse(&self) -> bool {
- self.0.is_some()
- }
-
- /// Returns the traversal root for the current traversal.
- pub(crate) fn traversal_root(self) -> Option<E> {
- self.0
- }
-}
-
-/// A global variable holding the state of
-/// `is_servo_nonincremental_layout()`.
-/// See [#22854](https://github.com/servo/servo/issues/22854).
-#[cfg(feature = "servo")]
-pub static IS_SERVO_NONINCREMENTAL_LAYOUT: std::sync::atomic::AtomicBool =
- std::sync::atomic::AtomicBool::new(false);
-
-#[cfg(feature = "servo")]
-#[inline]
-fn is_servo_nonincremental_layout() -> bool {
- use std::sync::atomic::Ordering;
-
- IS_SERVO_NONINCREMENTAL_LAYOUT.load(Ordering::Relaxed)
-}
-
-#[cfg(not(feature = "servo"))]
-#[inline]
-fn is_servo_nonincremental_layout() -> bool {
- false
-}
-
-/// A DOM Traversal trait, that is used to generically implement styling for
-/// Gecko and Servo.
-pub trait DomTraversal<E: TElement>: Sync {
- /// Process `node` on the way down, before its children have been processed.
- ///
- /// The callback is invoked for each child node that should be processed by
- /// the traversal.
- fn process_preorder<F>(
- &self,
- data: &PerLevelTraversalData,
- context: &mut StyleContext<E>,
- node: E::ConcreteNode,
- note_child: F,
- ) where
- F: FnMut(E::ConcreteNode);
-
- /// Process `node` on the way up, after its children have been processed.
- ///
- /// This is only executed if `needs_postorder_traversal` returns true.
- fn process_postorder(&self, contect: &mut StyleContext<E>, node: E::ConcreteNode);
-
- /// Boolean that specifies whether a bottom up traversal should be
- /// performed.
- ///
- /// If it's false, then process_postorder has no effect at all.
- fn needs_postorder_traversal() -> bool {
- true
- }
-
- /// Handles the postorder step of the traversal, if it exists, by bubbling
- /// up the parent chain.
- ///
- /// If we are the last child that finished processing, recursively process
- /// our parent. Else, stop. Also, stop at the root.
- ///
- /// Thus, if we start with all the leaves of a tree, we end up traversing
- /// the whole tree bottom-up because each parent will be processed exactly
- /// once (by the last child that finishes processing).
- ///
- /// The only communication between siblings is that they both
- /// fetch-and-subtract the parent's children count. This makes it safe to
- /// call durign the parallel traversal.
- fn handle_postorder_traversal(
- &self,
- context: &mut StyleContext<E>,
- root: OpaqueNode,
- mut node: E::ConcreteNode,
- children_to_process: isize,
- ) {
- // If the postorder step is a no-op, don't bother.
- if !Self::needs_postorder_traversal() {
- return;
- }
-
- if children_to_process == 0 {
- // We are a leaf. Walk up the chain.
- loop {
- self.process_postorder(context, node);
- if node.opaque() == root {
- break;
- }
- let parent = node.traversal_parent().unwrap();
- let remaining = parent.did_process_child();
- if remaining != 0 {
- // The parent has other unprocessed descendants. We only
- // perform postorder processing after the last descendant
- // has been processed.
- break;
- }
-
- node = parent.as_node();
- }
- } else {
- // Otherwise record the number of children to process when the time
- // comes.
- node.as_element()
- .unwrap()
- .store_children_to_process(children_to_process);
- }
- }
-
- /// Style invalidations happen when traversing from a parent to its children.
- /// However, this mechanism can't handle style invalidations on the root. As
- /// such, we have a pre-traversal step to handle that part and determine whether
- /// a full traversal is needed.
- fn pre_traverse(root: E, shared_context: &SharedStyleContext) -> PreTraverseToken<E> {
- use crate::invalidation::element::state_and_attributes::propagate_dirty_bit_up_to;
-
- let traversal_flags = shared_context.traversal_flags;
-
- let mut data = root.mutate_data();
- let mut data = data.as_mut().map(|d| &mut **d);
-
- if let Some(ref mut data) = data {
- if !traversal_flags.for_animation_only() {
- // Invalidate our style, and that of our siblings and
- // descendants as needed.
- let invalidation_result = data.invalidate_style_if_needed(
- root,
- shared_context,
- None,
- &mut NthIndexCache::default(),
- );
-
- if invalidation_result.has_invalidated_siblings() {
- let actual_root = root.as_node().parent_element_or_host().expect(
- "How in the world can you invalidate \
- siblings without a parent?",
- );
- propagate_dirty_bit_up_to(actual_root, root);
- return PreTraverseToken(Some(actual_root));
- }
- }
- }
-
- let should_traverse =
- Self::element_needs_traversal(root, traversal_flags, data.as_mut().map(|d| &**d));
-
- // If we're not going to traverse at all, we may need to clear some state
- // off the root (which would normally be done at the end of recalc_style_at).
- if !should_traverse && data.is_some() {
- clear_state_after_traversing(root, data.unwrap(), traversal_flags);
- }
-
- PreTraverseToken(if should_traverse { Some(root) } else { None })
- }
-
- /// Returns true if traversal should visit a text node. The style system
- /// never processes text nodes, but Servo overrides this to visit them for
- /// flow construction when necessary.
- fn text_node_needs_traversal(node: E::ConcreteNode, _parent_data: &ElementData) -> bool {
- debug_assert!(node.is_text_node());
- false
- }
-
- /// Returns true if traversal is needed for the given element and subtree.
- fn element_needs_traversal(
- el: E,
- traversal_flags: TraversalFlags,
- data: Option<&ElementData>,
- ) -> bool {
- debug!(
- "element_needs_traversal({:?}, {:?}, {:?})",
- el, traversal_flags, data
- );
-
- // In case of animation-only traversal we need to traverse the element if the element has
- // animation only dirty descendants bit, animation-only restyle hint.
- if traversal_flags.for_animation_only() {
- return data.map_or(false, |d| d.has_styles()) &&
- (el.has_animation_only_dirty_descendants() ||
- data.as_ref()
- .unwrap()
- .hint
- .has_animation_hint_or_recascade());
- }
-
- // Non-incremental layout visits every node.
- if is_servo_nonincremental_layout() {
- return true;
- }
-
- // Unwrap the data.
- let data = match data {
- Some(d) if d.has_styles() => d,
- _ => return true,
- };
-
- // If the dirty descendants bit is set, we need to traverse no matter
- // what. Skip examining the ElementData.
- if el.has_dirty_descendants() {
- return true;
- }
-
- // If we have a restyle hint or need to recascade, we need to visit the
- // element.
- //
- // Note that this is different than checking has_current_styles_for_traversal(),
- // since that can return true even if we have a restyle hint indicating
- // that the element's descendants (but not necessarily the element) need
- // restyling.
- if !data.hint.is_empty() {
- return true;
- }
-
- // Servo uses the post-order traversal for flow construction, so we need
- // to traverse any element with damage so that we can perform fixup /
- // reconstruction on our way back up the tree.
- if cfg!(feature = "servo") && !data.damage.is_empty() {
- return true;
- }
-
- trace!("{:?} doesn't need traversal", el);
- false
- }
-
- /// Return the shared style context common to all worker threads.
- fn shared_context(&self) -> &SharedStyleContext;
-}
-
-/// Manually resolve style by sequentially walking up the parent chain to the
-/// first styled Element, ignoring pending restyles. The resolved style is made
-/// available via a callback, and can be dropped by the time this function
-/// returns in the display:none subtree case.
-pub fn resolve_style<E>(
- context: &mut StyleContext<E>,
- element: E,
- rule_inclusion: RuleInclusion,
- pseudo: Option<&PseudoElement>,
- mut undisplayed_style_cache: Option<&mut UndisplayedStyleCache>,
-) -> ElementStyles
-where
- E: TElement,
-{
- debug_assert!(
- rule_inclusion == RuleInclusion::DefaultOnly ||
- pseudo.map_or(false, |p| p.is_before_or_after()) ||
- element.borrow_data().map_or(true, |d| !d.has_styles()),
- "Why are we here?"
- );
- debug_assert!(
- rule_inclusion == RuleInclusion::All || undisplayed_style_cache.is_none(),
- "can't use the cache for default styles only"
- );
-
- let mut ancestors_requiring_style_resolution = SmallVec::<[E; 16]>::new();
-
- // Clear the bloom filter, just in case the caller is reusing TLS.
- context.thread_local.bloom_filter.clear();
-
- let mut style = None;
- let mut ancestor = element.traversal_parent();
- while let Some(current) = ancestor {
- if rule_inclusion == RuleInclusion::All {
- if let Some(data) = current.borrow_data() {
- if let Some(ancestor_style) = data.styles.get_primary() {
- style = Some(ancestor_style.clone());
- break;
- }
- }
- }
- if let Some(ref mut cache) = undisplayed_style_cache {
- if let Some(s) = cache.get(&current.opaque()) {
- style = Some(s.clone());
- break;
- }
- }
- ancestors_requiring_style_resolution.push(current);
- ancestor = current.traversal_parent();
- }
-
- if let Some(ancestor) = ancestor {
- context.thread_local.bloom_filter.rebuild(ancestor);
- context.thread_local.bloom_filter.push(ancestor);
- }
-
- let mut layout_parent_style = style.clone();
- while let Some(style) = layout_parent_style.take() {
- if !style.is_display_contents() {
- layout_parent_style = Some(style);
- break;
- }
-
- ancestor = ancestor.unwrap().traversal_parent();
- layout_parent_style =
- ancestor.and_then(|a| a.borrow_data().map(|data| data.styles.primary().clone()));
- }
-
- for ancestor in ancestors_requiring_style_resolution.iter().rev() {
- context.thread_local.bloom_filter.assert_complete(*ancestor);
-
- // Actually `PseudoElementResolution` doesn't really matter here.
- // (but it does matter below!).
- let primary_style = StyleResolverForElement::new(
- *ancestor,
- context,
- rule_inclusion,
- PseudoElementResolution::IfApplicable,
- )
- .resolve_primary_style(style.as_deref(), layout_parent_style.as_deref());
-
- let is_display_contents = primary_style.style().is_display_contents();
-
- style = Some(primary_style.style.0);
- if !is_display_contents {
- layout_parent_style = style.clone();
- }
-
- if let Some(ref mut cache) = undisplayed_style_cache {
- cache.insert(ancestor.opaque(), style.clone().unwrap());
- }
- context.thread_local.bloom_filter.push(*ancestor);
- }
-
- context.thread_local.bloom_filter.assert_complete(element);
- let styles: ElementStyles = StyleResolverForElement::new(
- element,
- context,
- rule_inclusion,
- PseudoElementResolution::Force,
- )
- .resolve_style(style.as_deref(), layout_parent_style.as_deref())
- .into();
-
- if let Some(ref mut cache) = undisplayed_style_cache {
- cache.insert(element.opaque(), styles.primary().clone());
- }
-
- styles
-}
-
-/// Calculates the style for a single node.
-#[inline]
-#[allow(unsafe_code)]
-pub fn recalc_style_at<E, D, F>(
- _traversal: &D,
- traversal_data: &PerLevelTraversalData,
- context: &mut StyleContext<E>,
- element: E,
- data: &mut ElementData,
- note_child: F,
-) where
- E: TElement,
- D: DomTraversal<E>,
- F: FnMut(E::ConcreteNode),
-{
- use std::cmp;
-
- let flags = context.shared.traversal_flags;
- let is_initial_style = !data.has_styles();
-
- context.thread_local.statistics.elements_traversed += 1;
- debug_assert!(
- flags.intersects(TraversalFlags::AnimationOnly) ||
- !element.has_snapshot() ||
- element.handled_snapshot(),
- "Should've handled snapshots here already"
- );
-
- let restyle_kind = data.restyle_kind(&context.shared);
- debug!(
- "recalc_style_at: {:?} (restyle_kind={:?}, dirty_descendants={:?}, data={:?})",
- element,
- restyle_kind,
- element.has_dirty_descendants(),
- data
- );
-
- let mut child_restyle_requirement = ChildRestyleRequirement::CanSkipCascade;
-
- // Compute style for this element if necessary.
- if let Some(restyle_kind) = restyle_kind {
- child_restyle_requirement =
- compute_style(traversal_data, context, element, data, restyle_kind);
-
- if !element.matches_user_and_content_rules() {
- // We must always cascade native anonymous subtrees, since they
- // may have pseudo-elements underneath that would inherit from the
- // closest non-NAC ancestor instead of us.
- child_restyle_requirement = cmp::max(
- child_restyle_requirement,
- ChildRestyleRequirement::MustCascadeChildren,
- );
- }
-
- // If we're restyling this element to display:none, throw away all style
- // data in the subtree, notify the caller to early-return.
- if data.styles.is_display_none() {
- debug!(
- "{:?} style is display:none - clearing data from descendants.",
- element
- );
- unsafe {
- clear_descendant_data(element);
- }
- }
-
- // Inform any paint worklets of changed style, to speculatively
- // evaluate the worklet code. In the case that the size hasn't changed,
- // this will result in increased concurrency between script and layout.
- notify_paint_worklet(context, data);
- } else {
- debug_assert!(data.has_styles());
- data.set_traversed_without_styling();
- }
-
- // Now that matching and cascading is done, clear the bits corresponding to
- // those operations and compute the propagated restyle hint (unless we're
- // not processing invalidations, in which case don't need to propagate it
- // and must avoid clearing it).
- debug_assert!(
- flags.for_animation_only() || !data.hint.has_animation_hint(),
- "animation restyle hint should be handled during \
- animation-only restyles"
- );
- let mut propagated_hint = data.hint.propagate(&flags);
- trace!(
- "propagated_hint={:?}, restyle_requirement={:?}, \
- is_display_none={:?}, implementing_pseudo={:?}",
- propagated_hint,
- child_restyle_requirement,
- data.styles.is_display_none(),
- element.implemented_pseudo_element()
- );
-
- // Integrate the child cascade requirement into the propagated hint.
- match child_restyle_requirement {
- ChildRestyleRequirement::CanSkipCascade => {},
- ChildRestyleRequirement::MustCascadeDescendants => {
- propagated_hint |= RestyleHint::RECASCADE_SELF | RestyleHint::RECASCADE_DESCENDANTS;
- },
- ChildRestyleRequirement::MustCascadeChildrenIfInheritResetStyle => {
- propagated_hint |= RestyleHint::RECASCADE_SELF_IF_INHERIT_RESET_STYLE;
- },
- ChildRestyleRequirement::MustCascadeChildren => {
- propagated_hint |= RestyleHint::RECASCADE_SELF;
- },
- ChildRestyleRequirement::MustMatchDescendants => {
- propagated_hint |= RestyleHint::restyle_subtree();
- },
- }
-
- let has_dirty_descendants_for_this_restyle = if flags.for_animation_only() {
- element.has_animation_only_dirty_descendants()
- } else {
- element.has_dirty_descendants()
- };
-
- // Before examining each child individually, try to prove that our children
- // don't need style processing. They need processing if any of the following
- // conditions hold:
- //
- // * We have the dirty descendants bit.
- // * We're propagating a restyle hint.
- // * This is a servo non-incremental traversal.
- //
- // We only do this if we're not a display: none root, since in that case
- // it's useless to style children.
- let mut traverse_children = has_dirty_descendants_for_this_restyle ||
- !propagated_hint.is_empty() ||
- is_servo_nonincremental_layout();
-
- traverse_children = traverse_children && !data.styles.is_display_none();
-
- // Examine our children, and enqueue the appropriate ones for traversal.
- if traverse_children {
- note_children::<E, D, F>(
- context,
- element,
- data,
- propagated_hint,
- is_initial_style,
- note_child,
- );
- }
-
- // FIXME(bholley): Make these assertions pass for servo.
- if cfg!(feature = "gecko") && cfg!(debug_assertions) && data.styles.is_display_none() {
- debug_assert!(!element.has_dirty_descendants());
- debug_assert!(!element.has_animation_only_dirty_descendants());
- }
-
- clear_state_after_traversing(element, data, flags);
-}
-
-fn clear_state_after_traversing<E>(element: E, data: &mut ElementData, flags: TraversalFlags)
-where
- E: TElement,
-{
- if flags.intersects(TraversalFlags::FinalAnimationTraversal) {
- debug_assert!(flags.for_animation_only());
- data.clear_restyle_flags_and_damage();
- unsafe {
- element.unset_animation_only_dirty_descendants();
- }
- }
-}
-
-fn compute_style<E>(
- traversal_data: &PerLevelTraversalData,
- context: &mut StyleContext<E>,
- element: E,
- data: &mut ElementData,
- kind: RestyleKind,
-) -> ChildRestyleRequirement
-where
- E: TElement,
-{
- use crate::data::RestyleKind::*;
-
- context.thread_local.statistics.elements_styled += 1;
- debug!("compute_style: {:?} (kind={:?})", element, kind);
-
- if data.has_styles() {
- data.set_restyled();
- }
-
- let mut important_rules_changed = false;
- let new_styles = match kind {
- MatchAndCascade => {
- debug_assert!(
- !context.shared.traversal_flags.for_animation_only(),
- "MatchAndCascade shouldn't be processed during \
- animation-only traversal"
- );
- // Ensure the bloom filter is up to date.
- context
- .thread_local
- .bloom_filter
- .insert_parents_recovering(element, traversal_data.current_dom_depth);
-
- context.thread_local.bloom_filter.assert_complete(element);
- debug_assert_eq!(
- context.thread_local.bloom_filter.matching_depth(),
- traversal_data.current_dom_depth
- );
-
- // This is only relevant for animations as of right now.
- important_rules_changed = true;
-
- let mut target = StyleSharingTarget::new(element);
-
- // Now that our bloom filter is set up, try the style sharing
- // cache.
- match target.share_style_if_possible(context) {
- Some(shared_styles) => {
- context.thread_local.statistics.styles_shared += 1;
- shared_styles
- },
- None => {
- context.thread_local.statistics.elements_matched += 1;
- // Perform the matching and cascading.
- let new_styles = {
- let mut resolver = StyleResolverForElement::new(
- element,
- context,
- RuleInclusion::All,
- PseudoElementResolution::IfApplicable,
- );
-
- resolver.resolve_style_with_default_parents()
- };
-
- context.thread_local.sharing_cache.insert_if_possible(
- &element,
- &new_styles.primary,
- Some(&mut target),
- traversal_data.current_dom_depth,
- &context.shared,
- );
-
- new_styles
- },
- }
- },
- CascadeWithReplacements(flags) => {
- // Skipping full matching, load cascade inputs from previous values.
- let mut cascade_inputs = ElementCascadeInputs::new_from_element_data(data);
- important_rules_changed = element.replace_rules(flags, context, &mut cascade_inputs);
-
- let mut resolver = StyleResolverForElement::new(
- element,
- context,
- RuleInclusion::All,
- PseudoElementResolution::IfApplicable,
- );
-
- resolver.cascade_styles_with_default_parents(cascade_inputs)
- },
- CascadeOnly => {
- // Skipping full matching, load cascade inputs from previous values.
- let cascade_inputs = ElementCascadeInputs::new_from_element_data(data);
-
- let new_styles = {
- let mut resolver = StyleResolverForElement::new(
- element,
- context,
- RuleInclusion::All,
- PseudoElementResolution::IfApplicable,
- );
-
- resolver.cascade_styles_with_default_parents(cascade_inputs)
- };
-
- // Insert into the cache, but only if this style isn't reused from a
- // sibling or cousin. Otherwise, recascading a bunch of identical
- // elements would unnecessarily flood the cache with identical entries.
- //
- // This is analogous to the obvious "don't insert an element that just
- // got a hit in the style sharing cache" behavior in the MatchAndCascade
- // handling above.
- //
- // Note that, for the MatchAndCascade path, we still insert elements that
- // shared styles via the rule node, because we know that there's something
- // different about them that caused them to miss the sharing cache before
- // selector matching. If we didn't, we would still end up with the same
- // number of eventual styles, but would potentially miss out on various
- // opportunities for skipping selector matching, which could hurt
- // performance.
- if !new_styles.primary.reused_via_rule_node {
- context.thread_local.sharing_cache.insert_if_possible(
- &element,
- &new_styles.primary,
- None,
- traversal_data.current_dom_depth,
- &context.shared,
- );
- }
-
- new_styles
- },
- };
-
- element.finish_restyle(context, data, new_styles, important_rules_changed)
-}
-
-#[cfg(feature = "servo")]
-fn notify_paint_worklet<E>(context: &StyleContext<E>, data: &ElementData)
-where
- E: TElement,
-{
- use crate::values::generics::image::Image;
- use style_traits::ToCss;
-
- // We speculatively evaluate any paint worklets during styling.
- // This allows us to run paint worklets in parallel with style and layout.
- // Note that this is wasted effort if the size of the node has
- // changed, but in may cases it won't have.
- if let Some(ref values) = data.styles.primary {
- for image in &values.get_background().background_image.0 {
- let (name, arguments) = match *image {
- Image::PaintWorklet(ref worklet) => (&worklet.name, &worklet.arguments),
- _ => continue,
- };
- let painter = match context.shared.registered_speculative_painters.get(name) {
- Some(painter) => painter,
- None => continue,
- };
- let properties = painter
- .properties()
- .iter()
- .filter_map(|(name, id)| id.as_shorthand().err().map(|id| (name, id)))
- .map(|(name, id)| (name.clone(), values.computed_value_to_string(id)))
- .collect();
- let arguments = arguments
- .iter()
- .map(|argument| argument.to_css_string())
- .collect();
- debug!("Notifying paint worklet {}.", painter.name());
- painter.speculatively_draw_a_paint_image(properties, arguments);
- }
- }
-}
-
-#[cfg(not(feature = "servo"))]
-fn notify_paint_worklet<E>(_context: &StyleContext<E>, _data: &ElementData)
-where
- E: TElement,
-{
- // The CSS paint API is Servo-only at the moment
-}
-
-fn note_children<E, D, F>(
- context: &mut StyleContext<E>,
- element: E,
- data: &ElementData,
- propagated_hint: RestyleHint,
- is_initial_style: bool,
- mut note_child: F,
-) where
- E: TElement,
- D: DomTraversal<E>,
- F: FnMut(E::ConcreteNode),
-{
- trace!("note_children: {:?}", element);
- let flags = context.shared.traversal_flags;
-
- // Loop over all the traversal children.
- for child_node in element.traversal_children() {
- let child = match child_node.as_element() {
- Some(el) => el,
- None => {
- if is_servo_nonincremental_layout() ||
- D::text_node_needs_traversal(child_node, data)
- {
- note_child(child_node);
- }
- continue;
- },
- };
-
- let mut child_data = child.mutate_data();
- let mut child_data = child_data.as_mut().map(|d| &mut **d);
- trace!(
- " > {:?} -> {:?} + {:?}, pseudo: {:?}",
- child,
- child_data.as_ref().map(|d| d.hint),
- propagated_hint,
- child.implemented_pseudo_element()
- );
-
- if let Some(ref mut child_data) = child_data {
- child_data.hint.insert(propagated_hint);
-
- // Handle element snapshots and invalidation of descendants and siblings
- // as needed.
- //
- // NB: This will be a no-op if there's no snapshot.
- child_data.invalidate_style_if_needed(
- child,
- &context.shared,
- Some(&context.thread_local.stack_limit_checker),
- &mut context.thread_local.nth_index_cache,
- );
- }
-
- if D::element_needs_traversal(child, flags, child_data.map(|d| &*d)) {
- note_child(child_node);
-
- // Set the dirty descendants bit on the parent as needed, so that we
- // can find elements during the post-traversal.
- //
- // Note that these bits may be cleared again at the bottom of
- // recalc_style_at if requested by the caller.
- if !is_initial_style {
- if flags.for_animation_only() {
- unsafe {
- element.set_animation_only_dirty_descendants();
- }
- } else {
- unsafe {
- element.set_dirty_descendants();
- }
- }
- }
- }
- }
-}
-
-/// Clear style data for all the subtree under `root` (but not for root itself).
-///
-/// We use a list to avoid unbounded recursion, which we need to avoid in the
-/// parallel traversal because the rayon stacks are small.
-pub unsafe fn clear_descendant_data<E>(root: E)
-where
- E: TElement,
-{
- let mut parents = SmallVec::<[E; 32]>::new();
- parents.push(root);
- while let Some(p) = parents.pop() {
- for kid in p.traversal_children() {
- if let Some(kid) = kid.as_element() {
- // We maintain an invariant that, if an element has data, all its
- // ancestors have data as well.
- //
- // By consequence, any element without data has no descendants with
- // data.
- if kid.has_data() {
- kid.clear_data();
- parents.push(kid);
- }
- }
- }
- }
-
- // Make sure not to clear NODE_NEEDS_FRAME on the root.
- root.clear_descendant_bits();
-}
diff --git a/components/style/traversal_flags.rs b/components/style/traversal_flags.rs
deleted file mode 100644
index 0987230ed02..00000000000
--- a/components/style/traversal_flags.rs
+++ /dev/null
@@ -1,67 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
-
-//! Flags that control the traversal process.
-//!
-//! We CamelCase rather than UPPER_CASING so that we can grep for the same
-//! strings across gecko and servo.
-#![allow(non_upper_case_globals)]
-
-bitflags! {
- /// Flags that control the traversal process.
- pub struct TraversalFlags: u32 {
- /// Traverse only elements for animation restyles.
- const AnimationOnly = 1 << 0;
- /// Traverse and update all elements with CSS animations since
- /// @keyframes rules may have changed. Triggered by CSS rule changes.
- const ForCSSRuleChanges = 1 << 1;
- /// The final animation-only traversal, which shouldn't really care about other
- /// style changes anymore.
- const FinalAnimationTraversal = 1 << 2;
- /// Allows the traversal to run in parallel if there are sufficient cores on
- /// the machine.
- const ParallelTraversal = 1 << 7;
- /// Flush throttled animations. By default, we only update throttled animations
- /// when we have other non-throttled work to do. With this flag, we
- /// unconditionally tick and process them.
- const FlushThrottledAnimations = 1 << 8;
-
- }
-}
-
-/// Asserts that all TraversalFlags flags have a matching ServoTraversalFlags value in gecko.
-#[cfg(feature = "gecko")]
-#[inline]
-pub fn assert_traversal_flags_match() {
- use crate::gecko_bindings::structs;
-
- macro_rules! check_traversal_flags {
- ( $( $a:ident => $b:path ),*, ) => {
- if cfg!(debug_assertions) {
- let mut modes = TraversalFlags::all();
- $(
- assert_eq!(structs::$a as usize, $b.bits() as usize, stringify!($b));
- modes.remove($b);
- )*
- assert_eq!(modes, TraversalFlags::empty(), "all TraversalFlags bits should have an assertion");
- }
- }
- }
-
- check_traversal_flags! {
- ServoTraversalFlags_AnimationOnly => TraversalFlags::AnimationOnly,
- ServoTraversalFlags_ForCSSRuleChanges => TraversalFlags::ForCSSRuleChanges,
- ServoTraversalFlags_FinalAnimationTraversal => TraversalFlags::FinalAnimationTraversal,
- ServoTraversalFlags_ParallelTraversal => TraversalFlags::ParallelTraversal,
- ServoTraversalFlags_FlushThrottledAnimations => TraversalFlags::FlushThrottledAnimations,
- }
-}
-
-impl TraversalFlags {
- /// Returns true if the traversal is for animation-only restyles.
- #[inline]
- pub fn for_animation_only(&self) -> bool {
- self.contains(TraversalFlags::AnimationOnly)
- }
-}
diff --git a/components/style/use_counters/mod.rs b/components/style/use_counters/mod.rs
deleted file mode 100644
index bfa73291ee7..00000000000
--- a/components/style/use_counters/mod.rs
+++ /dev/null
@@ -1,96 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
-
-//! Various stuff for CSS property use counters.
-
-use crate::properties::{CountedUnknownProperty, COUNTED_UNKNOWN_PROPERTY_COUNT};
-use crate::properties::{NonCustomPropertyId, NON_CUSTOM_PROPERTY_ID_COUNT};
-use std::cell::Cell;
-
-#[cfg(target_pointer_width = "64")]
-const BITS_PER_ENTRY: usize = 64;
-
-#[cfg(target_pointer_width = "32")]
-const BITS_PER_ENTRY: usize = 32;
-
-/// One bit per each non-custom CSS property.
-#[derive(Default)]
-pub struct CountedUnknownPropertyUseCounters {
- storage: [Cell<usize>; (COUNTED_UNKNOWN_PROPERTY_COUNT - 1 + BITS_PER_ENTRY) / BITS_PER_ENTRY],
-}
-
-/// One bit per each non-custom CSS property.
-#[derive(Default)]
-pub struct NonCustomPropertyUseCounters {
- storage: [Cell<usize>; (NON_CUSTOM_PROPERTY_ID_COUNT - 1 + BITS_PER_ENTRY) / BITS_PER_ENTRY],
-}
-
-macro_rules! property_use_counters_methods {
- ($id: ident) => {
- /// Returns the bucket a given property belongs in, and the bitmask for that
- /// property.
- #[inline(always)]
- fn bucket_and_pattern(id: $id) -> (usize, usize) {
- let bit = id.bit();
- let bucket = bit / BITS_PER_ENTRY;
- let bit_in_bucket = bit % BITS_PER_ENTRY;
- (bucket, 1 << bit_in_bucket)
- }
-
- /// Record that a given property ID has been parsed.
- #[inline]
- pub fn record(&self, id: $id) {
- let (bucket, pattern) = Self::bucket_and_pattern(id);
- let bucket = &self.storage[bucket];
- bucket.set(bucket.get() | pattern)
- }
-
- /// Returns whether a given property ID has been recorded
- /// earlier.
- #[inline]
- pub fn recorded(&self, id: $id) -> bool {
- let (bucket, pattern) = Self::bucket_and_pattern(id);
- self.storage[bucket].get() & pattern != 0
- }
-
- /// Merge `other` into `self`.
- #[inline]
- fn merge(&self, other: &Self) {
- for (bucket, other_bucket) in self.storage.iter().zip(other.storage.iter()) {
- bucket.set(bucket.get() | other_bucket.get())
- }
- }
- };
-}
-
-impl CountedUnknownPropertyUseCounters {
- property_use_counters_methods!(CountedUnknownProperty);
-}
-
-impl NonCustomPropertyUseCounters {
- property_use_counters_methods!(NonCustomPropertyId);
-}
-
-/// The use-counter data related to a given document we want to store.
-#[derive(Default)]
-pub struct UseCounters {
- /// The counters for non-custom properties that have been parsed in the
- /// document's stylesheets.
- pub non_custom_properties: NonCustomPropertyUseCounters,
- /// The counters for css properties which we haven't implemented yet.
- pub counted_unknown_properties: CountedUnknownPropertyUseCounters,
-}
-
-impl UseCounters {
- /// Merge the use counters.
- ///
- /// Used for parallel parsing, where we parse off-main-thread.
- #[inline]
- pub fn merge(&self, other: &Self) {
- self.non_custom_properties
- .merge(&other.non_custom_properties);
- self.counted_unknown_properties
- .merge(&other.counted_unknown_properties);
- }
-}
diff --git a/components/style/values/animated/color.rs b/components/style/values/animated/color.rs
deleted file mode 100644
index 3e05ba724de..00000000000
--- a/components/style/values/animated/color.rs
+++ /dev/null
@@ -1,88 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
-
-//! Animated types for CSS colors.
-
-use crate::color::mix::ColorInterpolationMethod;
-use crate::color::AbsoluteColor;
-use crate::values::animated::{Animate, Procedure, ToAnimatedZero};
-use crate::values::computed::Percentage;
-use crate::values::distance::{ComputeSquaredDistance, SquaredDistance};
-use crate::values::generics::color::{GenericColor, GenericColorMix};
-
-impl Animate for AbsoluteColor {
- #[inline]
- fn animate(&self, other: &Self, procedure: Procedure) -> Result<Self, ()> {
- let (left_weight, right_weight) = procedure.weights();
- Ok(crate::color::mix::mix(
- ColorInterpolationMethod::best_interpolation_between(self, other),
- self,
- left_weight as f32,
- other,
- right_weight as f32,
- /* normalize_weights = */ false,
- ))
- }
-}
-
-impl ComputeSquaredDistance for AbsoluteColor {
- #[inline]
- fn compute_squared_distance(&self, other: &Self) -> Result<SquaredDistance, ()> {
- let start = [
- self.alpha,
- self.components.0 * self.alpha,
- self.components.1 * self.alpha,
- self.components.2 * self.alpha,
- ];
- let end = [
- other.alpha,
- other.components.0 * other.alpha,
- other.components.1 * other.alpha,
- other.components.2 * other.alpha,
- ];
- start
- .iter()
- .zip(&end)
- .map(|(this, other)| this.compute_squared_distance(other))
- .sum()
- }
-}
-
-/// An animated value for `<color>`.
-pub type Color = GenericColor<Percentage>;
-
-/// An animated value for `<color-mix>`.
-pub type ColorMix = GenericColorMix<Color, Percentage>;
-
-impl Animate for Color {
- #[inline]
- fn animate(&self, other: &Self, procedure: Procedure) -> Result<Self, ()> {
- let (left_weight, right_weight) = procedure.weights();
- Ok(Self::from_color_mix(ColorMix {
- interpolation: ColorInterpolationMethod::srgb(),
- left: self.clone(),
- left_percentage: Percentage(left_weight as f32),
- right: other.clone(),
- right_percentage: Percentage(right_weight as f32),
- // See https://github.com/w3c/csswg-drafts/issues/7324
- normalize_weights: false,
- }))
- }
-}
-
-impl ComputeSquaredDistance for Color {
- #[inline]
- fn compute_squared_distance(&self, other: &Self) -> Result<SquaredDistance, ()> {
- let current_color = AbsoluteColor::transparent();
- self.resolve_to_absolute(&current_color)
- .compute_squared_distance(&other.resolve_to_absolute(&current_color))
- }
-}
-
-impl ToAnimatedZero for Color {
- #[inline]
- fn to_animated_zero(&self) -> Result<Self, ()> {
- Ok(Color::Absolute(AbsoluteColor::transparent()))
- }
-}
diff --git a/components/style/values/animated/effects.rs b/components/style/values/animated/effects.rs
deleted file mode 100644
index e2b37cecbe7..00000000000
--- a/components/style/values/animated/effects.rs
+++ /dev/null
@@ -1,27 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
-
-//! Animated types for CSS values related to effects.
-
-use crate::values::animated::color::Color;
-use crate::values::computed::length::Length;
-#[cfg(feature = "gecko")]
-use crate::values::computed::url::ComputedUrl;
-use crate::values::computed::{Angle, Number};
-use crate::values::generics::effects::Filter as GenericFilter;
-use crate::values::generics::effects::SimpleShadow as GenericSimpleShadow;
-#[cfg(not(feature = "gecko"))]
-use crate::values::Impossible;
-
-/// An animated value for the `drop-shadow()` filter.
-pub type AnimatedSimpleShadow = GenericSimpleShadow<Color, Length, Length>;
-
-/// An animated value for a single `filter`.
-#[cfg(feature = "gecko")]
-pub type AnimatedFilter =
- GenericFilter<Angle, Number, Number, Length, AnimatedSimpleShadow, ComputedUrl>;
-
-/// An animated value for a single `filter`.
-#[cfg(not(feature = "gecko"))]
-pub type AnimatedFilter = GenericFilter<Angle, Number, Number, Length, AnimatedSimpleShadow, Impossible>;
diff --git a/components/style/values/animated/font.rs b/components/style/values/animated/font.rs
deleted file mode 100644
index 63d4a14b2fe..00000000000
--- a/components/style/values/animated/font.rs
+++ /dev/null
@@ -1,37 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
-
-//! Animation implementation for various font-related types.
-
-use super::{Animate, Procedure, ToAnimatedZero};
-use crate::values::computed::font::FontVariationSettings;
-use crate::values::distance::{ComputeSquaredDistance, SquaredDistance};
-
-/// <https://drafts.csswg.org/css-fonts-4/#font-variation-settings-def>
-///
-/// Note that the ComputedValue implementation will already have sorted and de-dup'd
-/// the lists of settings, so we can just iterate over the two lists together and
-/// animate their individual values.
-impl Animate for FontVariationSettings {
- #[inline]
- fn animate(&self, other: &Self, procedure: Procedure) -> Result<Self, ()> {
- let result: Vec<_> =
- super::lists::by_computed_value::animate(&self.0, &other.0, procedure)?;
- Ok(Self(result.into_boxed_slice()))
- }
-}
-
-impl ComputeSquaredDistance for FontVariationSettings {
- #[inline]
- fn compute_squared_distance(&self, other: &Self) -> Result<SquaredDistance, ()> {
- super::lists::by_computed_value::squared_distance(&self.0, &other.0)
- }
-}
-
-impl ToAnimatedZero for FontVariationSettings {
- #[inline]
- fn to_animated_zero(&self) -> Result<Self, ()> {
- Err(())
- }
-}
diff --git a/components/style/values/animated/grid.rs b/components/style/values/animated/grid.rs
deleted file mode 100644
index 8136ba5ece3..00000000000
--- a/components/style/values/animated/grid.rs
+++ /dev/null
@@ -1,157 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
-
-//! Animation implementation for various grid-related types.
-
-// Note: we can implement Animate on their generic types directly, but in this case we need to
-// make sure two trait bounds, L: Clone and I: PartialEq, are satisfied on almost all the
-// grid-related types and their other trait implementations because Animate needs them. So in
-// order to avoid adding these two trait bounds (or maybe more..) everywhere, we implement
-// Animate for the computed types, instead of the generic types.
-
-use super::{Animate, Procedure, ToAnimatedZero};
-use crate::values::computed::Integer;
-use crate::values::computed::LengthPercentage;
-use crate::values::computed::{GridTemplateComponent, TrackList, TrackSize};
-use crate::values::distance::{ComputeSquaredDistance, SquaredDistance};
-use crate::values::generics::grid as generics;
-
-fn discrete<T: Clone>(from: &T, to: &T, procedure: Procedure) -> Result<T, ()> {
- if let Procedure::Interpolate { progress } = procedure {
- Ok(if progress < 0.5 {
- from.clone()
- } else {
- to.clone()
- })
- } else {
- Err(())
- }
-}
-
-fn animate_with_discrete_fallback<T: Animate + Clone>(
- from: &T,
- to: &T,
- procedure: Procedure,
-) -> Result<T, ()> {
- from.animate(to, procedure)
- .or_else(|_| discrete(from, to, procedure))
-}
-
-impl Animate for TrackSize {
- fn animate(&self, other: &Self, procedure: Procedure) -> Result<Self, ()> {
- match (self, other) {
- (&generics::TrackSize::Breadth(ref from), &generics::TrackSize::Breadth(ref to)) => {
- animate_with_discrete_fallback(from, to, procedure)
- .map(generics::TrackSize::Breadth)
- },
- (
- &generics::TrackSize::Minmax(ref from_min, ref from_max),
- &generics::TrackSize::Minmax(ref to_min, ref to_max),
- ) => Ok(generics::TrackSize::Minmax(
- animate_with_discrete_fallback(from_min, to_min, procedure)?,
- animate_with_discrete_fallback(from_max, to_max, procedure)?,
- )),
- (
- &generics::TrackSize::FitContent(ref from),
- &generics::TrackSize::FitContent(ref to),
- ) => animate_with_discrete_fallback(from, to, procedure)
- .map(generics::TrackSize::FitContent),
- (_, _) => discrete(self, other, procedure),
- }
- }
-}
-
-impl Animate for generics::TrackRepeat<LengthPercentage, Integer> {
- fn animate(&self, other: &Self, procedure: Procedure) -> Result<Self, ()> {
- // If the keyword, auto-fit/fill, is the same it can result in different
- // number of tracks. For both auto-fit/fill, the number of columns isn't
- // known until you do layout since it depends on the container size, item
- // placement and other factors, so we cannot do the correct interpolation
- // by computed values. Therefore, return Err(()) if it's keywords. If it
- // is Number, we support animation only if the count is the same and the
- // length of track_sizes is the same.
- // https://github.com/w3c/csswg-drafts/issues/3503
- match (&self.count, &other.count) {
- (&generics::RepeatCount::Number(from), &generics::RepeatCount::Number(to))
- if from == to =>
- {
- ()
- },
- (_, _) => return Err(()),
- }
-
- let count = self.count;
- let track_sizes = super::lists::by_computed_value::animate(
- &self.track_sizes,
- &other.track_sizes,
- procedure,
- )?;
-
- // The length of |line_names| is always 0 or N+1, where N is the length
- // of |track_sizes|. Besides, <line-names> is always discrete.
- let line_names = discrete(&self.line_names, &other.line_names, procedure)?;
-
- Ok(generics::TrackRepeat {
- count,
- line_names,
- track_sizes,
- })
- }
-}
-
-impl Animate for TrackList {
- // Based on https://github.com/w3c/csswg-drafts/issues/3201:
- // 1. Check interpolation type per track, so we need to handle discrete animations
- // in TrackSize, so any Err(()) returned from TrackSize doesn't make all TrackSize
- // fallback to discrete animation.
- // 2. line-names is always discrete.
- fn animate(&self, other: &Self, procedure: Procedure) -> Result<Self, ()> {
- if self.values.len() != other.values.len() {
- return Err(());
- }
-
- if self.is_explicit() != other.is_explicit() {
- return Err(());
- }
-
- // For now, repeat(auto-fill/auto-fit, ...) is not animatable.
- // TrackRepeat will return Err(()) if we use keywords. Therefore, we can
- // early return here to avoid traversing |values| in <auto-track-list>.
- // This may be updated in the future.
- // https://github.com/w3c/csswg-drafts/issues/3503
- if self.has_auto_repeat() || other.has_auto_repeat() {
- return Err(());
- }
-
- let values =
- super::lists::by_computed_value::animate(&self.values, &other.values, procedure)?;
-
- // The length of |line_names| is always 0 or N+1, where N is the length
- // of |track_sizes|. Besides, <line-names> is always discrete.
- let line_names = discrete(&self.line_names, &other.line_names, procedure)?;
-
- Ok(TrackList {
- values,
- line_names,
- auto_repeat_index: self.auto_repeat_index,
- })
- }
-}
-
-impl ComputeSquaredDistance for GridTemplateComponent {
- #[inline]
- fn compute_squared_distance(&self, _other: &Self) -> Result<SquaredDistance, ()> {
- // TODO: Bug 1518585, we should implement ComputeSquaredDistance.
- Err(())
- }
-}
-
-impl ToAnimatedZero for GridTemplateComponent {
- #[inline]
- fn to_animated_zero(&self) -> Result<Self, ()> {
- // It's not clear to get a zero grid track list based on the current definition
- // of spec, so we return Err(()) directly.
- Err(())
- }
-}
diff --git a/components/style/values/animated/lists.rs b/components/style/values/animated/lists.rs
deleted file mode 100644
index 8b3898c4971..00000000000
--- a/components/style/values/animated/lists.rs
+++ /dev/null
@@ -1,141 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
-
-//! Lists have various ways of being animated, this module implements them.
-//!
-//! See https://drafts.csswg.org/web-animations-1/#animating-properties
-
-/// https://drafts.csswg.org/web-animations-1/#by-computed-value
-pub mod by_computed_value {
- use crate::values::{
- animated::{Animate, Procedure},
- distance::{ComputeSquaredDistance, SquaredDistance},
- };
- use std::iter::FromIterator;
-
- #[allow(missing_docs)]
- pub fn animate<T, C>(left: &[T], right: &[T], procedure: Procedure) -> Result<C, ()>
- where
- T: Animate,
- C: FromIterator<T>,
- {
- if left.len() != right.len() {
- return Err(());
- }
- left.iter()
- .zip(right.iter())
- .map(|(left, right)| left.animate(right, procedure))
- .collect()
- }
-
- #[allow(missing_docs)]
- pub fn squared_distance<T>(left: &[T], right: &[T]) -> Result<SquaredDistance, ()>
- where
- T: ComputeSquaredDistance,
- {
- if left.len() != right.len() {
- return Err(());
- }
- left.iter()
- .zip(right.iter())
- .map(|(left, right)| left.compute_squared_distance(right))
- .sum()
- }
-}
-
-/// This is the animation used for some of the types like shadows and filters, where the
-/// interpolation happens with the zero value if one of the sides is not present.
-///
-/// https://drafts.csswg.org/web-animations-1/#animating-shadow-lists
-pub mod with_zero {
- use crate::values::animated::ToAnimatedZero;
- use crate::values::{
- animated::{Animate, Procedure},
- distance::{ComputeSquaredDistance, SquaredDistance},
- };
- use itertools::{EitherOrBoth, Itertools};
- use std::iter::FromIterator;
-
- #[allow(missing_docs)]
- pub fn animate<T, C>(left: &[T], right: &[T], procedure: Procedure) -> Result<C, ()>
- where
- T: Animate + Clone + ToAnimatedZero,
- C: FromIterator<T>,
- {
- if procedure == Procedure::Add {
- return Ok(left.iter().chain(right.iter()).cloned().collect());
- }
- left.iter()
- .zip_longest(right.iter())
- .map(|it| match it {
- EitherOrBoth::Both(left, right) => left.animate(right, procedure),
- EitherOrBoth::Left(left) => left.animate(&left.to_animated_zero()?, procedure),
- EitherOrBoth::Right(right) => right.to_animated_zero()?.animate(right, procedure),
- })
- .collect()
- }
-
- #[allow(missing_docs)]
- pub fn squared_distance<T>(left: &[T], right: &[T]) -> Result<SquaredDistance, ()>
- where
- T: ToAnimatedZero + ComputeSquaredDistance,
- {
- left.iter()
- .zip_longest(right.iter())
- .map(|it| match it {
- EitherOrBoth::Both(left, right) => left.compute_squared_distance(right),
- EitherOrBoth::Left(item) | EitherOrBoth::Right(item) => {
- item.to_animated_zero()?.compute_squared_distance(item)
- },
- })
- .sum()
- }
-}
-
-/// https://drafts.csswg.org/web-animations-1/#repeatable-list
-pub mod repeatable_list {
- use crate::values::{
- animated::{Animate, Procedure},
- distance::{ComputeSquaredDistance, SquaredDistance},
- };
- use std::iter::FromIterator;
-
- #[allow(missing_docs)]
- pub fn animate<T, C>(left: &[T], right: &[T], procedure: Procedure) -> Result<C, ()>
- where
- T: Animate,
- C: FromIterator<T>,
- {
- use num_integer::lcm;
- // If the length of either list is zero, the least common multiple is undefined.
- if left.is_empty() || right.is_empty() {
- return Err(());
- }
- let len = lcm(left.len(), right.len());
- left.iter()
- .cycle()
- .zip(right.iter().cycle())
- .take(len)
- .map(|(left, right)| left.animate(right, procedure))
- .collect()
- }
-
- #[allow(missing_docs)]
- pub fn squared_distance<T>(left: &[T], right: &[T]) -> Result<SquaredDistance, ()>
- where
- T: ComputeSquaredDistance,
- {
- use num_integer::lcm;
- if left.is_empty() || right.is_empty() {
- return Err(());
- }
- let len = lcm(left.len(), right.len());
- left.iter()
- .cycle()
- .zip(right.iter().cycle())
- .take(len)
- .map(|(left, right)| left.compute_squared_distance(right))
- .sum()
- }
-}
diff --git a/components/style/values/animated/mod.rs b/components/style/values/animated/mod.rs
deleted file mode 100644
index 04a05a81ac4..00000000000
--- a/components/style/values/animated/mod.rs
+++ /dev/null
@@ -1,488 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
-
-//! Animated values.
-//!
-//! Some values, notably colors, cannot be interpolated directly with their
-//! computed values and need yet another intermediate representation. This
-//! module's raison d'être is to ultimately contain all these types.
-
-use crate::color::AbsoluteColor;
-use crate::properties::PropertyId;
-use crate::values::computed::length::LengthPercentage;
-use crate::values::computed::url::ComputedUrl;
-use crate::values::computed::Angle as ComputedAngle;
-use crate::values::computed::Image;
-use crate::values::specified::SVGPathData;
-use crate::values::CSSFloat;
-use app_units::Au;
-use smallvec::SmallVec;
-use std::cmp;
-
-pub mod color;
-pub mod effects;
-mod font;
-mod grid;
-pub mod lists;
-mod svg;
-pub mod transform;
-
-/// The category a property falls into for ordering purposes.
-///
-/// https://drafts.csswg.org/web-animations/#calculating-computed-keyframes
-#[derive(Clone, Copy, Eq, Ord, PartialEq, PartialOrd)]
-enum PropertyCategory {
- Custom,
- PhysicalLonghand,
- LogicalLonghand,
- Shorthand,
-}
-
-impl PropertyCategory {
- fn of(id: &PropertyId) -> Self {
- match *id {
- PropertyId::Shorthand(..) | PropertyId::ShorthandAlias(..) => {
- PropertyCategory::Shorthand
- },
- PropertyId::Longhand(id) | PropertyId::LonghandAlias(id, ..) => {
- if id.is_logical() {
- PropertyCategory::LogicalLonghand
- } else {
- PropertyCategory::PhysicalLonghand
- }
- },
- PropertyId::Custom(..) => PropertyCategory::Custom,
- }
- }
-}
-
-/// A comparator to sort PropertyIds such that physical longhands are sorted
-/// before logical longhands and shorthands, shorthands with fewer components
-/// are sorted before shorthands with more components, and otherwise shorthands
-/// are sorted by IDL name as defined by [Web Animations][property-order].
-///
-/// Using this allows us to prioritize values specified by longhands (or smaller
-/// shorthand subsets) when longhands and shorthands are both specified on the
-/// one keyframe.
-///
-/// [property-order] https://drafts.csswg.org/web-animations/#calculating-computed-keyframes
-pub fn compare_property_priority(a: &PropertyId, b: &PropertyId) -> cmp::Ordering {
- let a_category = PropertyCategory::of(a);
- let b_category = PropertyCategory::of(b);
-
- if a_category != b_category {
- return a_category.cmp(&b_category);
- }
-
- if a_category != PropertyCategory::Shorthand {
- return cmp::Ordering::Equal;
- }
-
- let a = a.as_shorthand().unwrap();
- let b = b.as_shorthand().unwrap();
- // Within shorthands, sort by the number of subproperties, then by IDL
- // name.
- let subprop_count_a = a.longhands().count();
- let subprop_count_b = b.longhands().count();
- subprop_count_a
- .cmp(&subprop_count_b)
- .then_with(|| a.idl_name_sort_order().cmp(&b.idl_name_sort_order()))
-}
-
-/// A helper function to animate two multiplicative factor.
-pub fn animate_multiplicative_factor(
- this: CSSFloat,
- other: CSSFloat,
- procedure: Procedure,
-) -> Result<CSSFloat, ()> {
- Ok((this - 1.).animate(&(other - 1.), procedure)? + 1.)
-}
-
-/// Animate from one value to another.
-///
-/// This trait is derivable with `#[derive(Animate)]`. The derived
-/// implementation uses a `match` expression with identical patterns for both
-/// `self` and `other`, calling `Animate::animate` on each fields of the values.
-/// If a field is annotated with `#[animation(constant)]`, the two values should
-/// be equal or an error is returned.
-///
-/// If a variant is annotated with `#[animation(error)]`, the corresponding
-/// `match` arm returns an error.
-///
-/// Trait bounds for type parameter `Foo` can be opted out of with
-/// `#[animation(no_bound(Foo))]` on the type definition, trait bounds for
-/// fields can be opted into with `#[animation(field_bound)]` on the field.
-pub trait Animate: Sized {
- /// Animate a value towards another one, given an animation procedure.
- fn animate(&self, other: &Self, procedure: Procedure) -> Result<Self, ()>;
-}
-
-/// An animation procedure.
-///
-/// <https://drafts.csswg.org/web-animations/#procedures-for-animating-properties>
-#[allow(missing_docs)]
-#[derive(Clone, Copy, Debug, PartialEq)]
-pub enum Procedure {
- /// <https://drafts.csswg.org/web-animations/#animation-interpolation>
- Interpolate { progress: f64 },
- /// <https://drafts.csswg.org/web-animations/#animation-addition>
- Add,
- /// <https://drafts.csswg.org/web-animations/#animation-accumulation>
- Accumulate { count: u64 },
-}
-
-/// Conversion between computed values and intermediate values for animations.
-///
-/// Notably, colors are represented as four floats during animations.
-///
-/// This trait is derivable with `#[derive(ToAnimatedValue)]`.
-pub trait ToAnimatedValue {
- /// The type of the animated value.
- type AnimatedValue;
-
- /// Converts this value to an animated value.
- fn to_animated_value(self) -> Self::AnimatedValue;
-
- /// Converts back an animated value into a computed value.
- fn from_animated_value(animated: Self::AnimatedValue) -> Self;
-}
-
-/// Returns a value similar to `self` that represents zero.
-///
-/// This trait is derivable with `#[derive(ToAnimatedValue)]`. If a field is
-/// annotated with `#[animation(constant)]`, a clone of its value will be used
-/// instead of calling `ToAnimatedZero::to_animated_zero` on it.
-///
-/// If a variant is annotated with `#[animation(error)]`, the corresponding
-/// `match` arm is not generated.
-///
-/// Trait bounds for type parameter `Foo` can be opted out of with
-/// `#[animation(no_bound(Foo))]` on the type definition.
-pub trait ToAnimatedZero: Sized {
- /// Returns a value that, when added with an underlying value, will produce the underlying
- /// value. This is used for SMIL animation's "by-animation" where SMIL first interpolates from
- /// the zero value to the 'by' value, and then adds the result to the underlying value.
- ///
- /// This is not the necessarily the same as the initial value of a property. For example, the
- /// initial value of 'stroke-width' is 1, but the zero value is 0, since adding 1 to the
- /// underlying value will not produce the underlying value.
- fn to_animated_zero(&self) -> Result<Self, ()>;
-}
-
-impl Procedure {
- /// Returns this procedure as a pair of weights.
- ///
- /// This is useful for animations that don't animate differently
- /// depending on the used procedure.
- #[inline]
- pub fn weights(self) -> (f64, f64) {
- match self {
- Procedure::Interpolate { progress } => (1. - progress, progress),
- Procedure::Add => (1., 1.),
- Procedure::Accumulate { count } => (count as f64, 1.),
- }
- }
-}
-
-/// <https://drafts.csswg.org/css-transitions/#animtype-number>
-impl Animate for i32 {
- #[inline]
- fn animate(&self, other: &Self, procedure: Procedure) -> Result<Self, ()> {
- Ok(((*self as f64).animate(&(*other as f64), procedure)? + 0.5).floor() as i32)
- }
-}
-
-/// <https://drafts.csswg.org/css-transitions/#animtype-number>
-impl Animate for f32 {
- #[inline]
- fn animate(&self, other: &Self, procedure: Procedure) -> Result<Self, ()> {
- let ret = (*self as f64).animate(&(*other as f64), procedure)?;
- Ok(ret.min(f32::MAX as f64).max(f32::MIN as f64) as f32)
- }
-}
-
-/// <https://drafts.csswg.org/css-transitions/#animtype-number>
-impl Animate for f64 {
- #[inline]
- fn animate(&self, other: &Self, procedure: Procedure) -> Result<Self, ()> {
- let (self_weight, other_weight) = procedure.weights();
-
- let ret = *self * self_weight + *other * other_weight;
- Ok(ret.min(f64::MAX).max(f64::MIN))
- }
-}
-
-impl<T> Animate for Option<T>
-where
- T: Animate,
-{
- #[inline]
- fn animate(&self, other: &Self, procedure: Procedure) -> Result<Self, ()> {
- match (self.as_ref(), other.as_ref()) {
- (Some(ref this), Some(ref other)) => Ok(Some(this.animate(other, procedure)?)),
- (None, None) => Ok(None),
- _ => Err(()),
- }
- }
-}
-
-impl Animate for Au {
- #[inline]
- fn animate(&self, other: &Self, procedure: Procedure) -> Result<Self, ()> {
- Ok(Au::new(self.0.animate(&other.0, procedure)?))
- }
-}
-
-impl<T: Animate> Animate for Box<T> {
- #[inline]
- fn animate(&self, other: &Self, procedure: Procedure) -> Result<Self, ()> {
- Ok(Box::new((**self).animate(&other, procedure)?))
- }
-}
-
-impl<T> ToAnimatedValue for Option<T>
-where
- T: ToAnimatedValue,
-{
- type AnimatedValue = Option<<T as ToAnimatedValue>::AnimatedValue>;
-
- #[inline]
- fn to_animated_value(self) -> Self::AnimatedValue {
- self.map(T::to_animated_value)
- }
-
- #[inline]
- fn from_animated_value(animated: Self::AnimatedValue) -> Self {
- animated.map(T::from_animated_value)
- }
-}
-
-impl<T> ToAnimatedValue for Vec<T>
-where
- T: ToAnimatedValue,
-{
- type AnimatedValue = Vec<<T as ToAnimatedValue>::AnimatedValue>;
-
- #[inline]
- fn to_animated_value(self) -> Self::AnimatedValue {
- self.into_iter().map(T::to_animated_value).collect()
- }
-
- #[inline]
- fn from_animated_value(animated: Self::AnimatedValue) -> Self {
- animated.into_iter().map(T::from_animated_value).collect()
- }
-}
-
-impl<T> ToAnimatedValue for Box<T>
-where
- T: ToAnimatedValue,
-{
- type AnimatedValue = Box<<T as ToAnimatedValue>::AnimatedValue>;
-
- #[inline]
- fn to_animated_value(self) -> Self::AnimatedValue {
- Box::new((*self).to_animated_value())
- }
-
- #[inline]
- fn from_animated_value(animated: Self::AnimatedValue) -> Self {
- Box::new(T::from_animated_value(*animated))
- }
-}
-
-impl<T> ToAnimatedValue for Box<[T]>
-where
- T: ToAnimatedValue,
-{
- type AnimatedValue = Box<[<T as ToAnimatedValue>::AnimatedValue]>;
-
- #[inline]
- fn to_animated_value(self) -> Self::AnimatedValue {
- self.into_vec()
- .into_iter()
- .map(T::to_animated_value)
- .collect::<Vec<_>>()
- .into_boxed_slice()
- }
-
- #[inline]
- fn from_animated_value(animated: Self::AnimatedValue) -> Self {
- animated
- .into_vec()
- .into_iter()
- .map(T::from_animated_value)
- .collect::<Vec<_>>()
- .into_boxed_slice()
- }
-}
-
-impl<T> ToAnimatedValue for crate::OwnedSlice<T>
-where
- T: ToAnimatedValue,
-{
- type AnimatedValue = crate::OwnedSlice<<T as ToAnimatedValue>::AnimatedValue>;
-
- #[inline]
- fn to_animated_value(self) -> Self::AnimatedValue {
- self.into_box().to_animated_value().into()
- }
-
- #[inline]
- fn from_animated_value(animated: Self::AnimatedValue) -> Self {
- Self::from(Box::from_animated_value(animated.into_box()))
- }
-}
-
-impl<T> ToAnimatedValue for SmallVec<[T; 1]>
-where
- T: ToAnimatedValue,
-{
- type AnimatedValue = SmallVec<[T::AnimatedValue; 1]>;
-
- #[inline]
- fn to_animated_value(self) -> Self::AnimatedValue {
- self.into_iter().map(T::to_animated_value).collect()
- }
-
- #[inline]
- fn from_animated_value(animated: Self::AnimatedValue) -> Self {
- animated.into_iter().map(T::from_animated_value).collect()
- }
-}
-
-macro_rules! trivial_to_animated_value {
- ($ty:ty) => {
- impl $crate::values::animated::ToAnimatedValue for $ty {
- type AnimatedValue = Self;
-
- #[inline]
- fn to_animated_value(self) -> Self {
- self
- }
-
- #[inline]
- fn from_animated_value(animated: Self::AnimatedValue) -> Self {
- animated
- }
- }
- };
-}
-
-trivial_to_animated_value!(Au);
-trivial_to_animated_value!(LengthPercentage);
-trivial_to_animated_value!(ComputedAngle);
-trivial_to_animated_value!(ComputedUrl);
-trivial_to_animated_value!(bool);
-trivial_to_animated_value!(f32);
-trivial_to_animated_value!(i32);
-trivial_to_animated_value!(AbsoluteColor);
-// Note: This implementation is for ToAnimatedValue of ShapeSource.
-//
-// SVGPathData uses Box<[T]>. If we want to derive ToAnimatedValue for all the
-// types, we have to do "impl ToAnimatedValue for Box<[T]>" first.
-// However, the general version of "impl ToAnimatedValue for Box<[T]>" needs to
-// clone |T| and convert it into |T::AnimatedValue|. However, for SVGPathData
-// that is unnecessary--moving |T| is sufficient. So here, we implement this
-// trait manually.
-trivial_to_animated_value!(SVGPathData);
-// FIXME: Bug 1514342, Image is not animatable, but we still need to implement
-// this to avoid adding this derive to generic::Image and all its arms. We can
-// drop this after landing Bug 1514342.
-trivial_to_animated_value!(Image);
-
-impl ToAnimatedZero for Au {
- #[inline]
- fn to_animated_zero(&self) -> Result<Self, ()> {
- Ok(Au(0))
- }
-}
-
-impl ToAnimatedZero for f32 {
- #[inline]
- fn to_animated_zero(&self) -> Result<Self, ()> {
- Ok(0.)
- }
-}
-
-impl ToAnimatedZero for f64 {
- #[inline]
- fn to_animated_zero(&self) -> Result<Self, ()> {
- Ok(0.)
- }
-}
-
-impl ToAnimatedZero for i32 {
- #[inline]
- fn to_animated_zero(&self) -> Result<Self, ()> {
- Ok(0)
- }
-}
-
-impl<T> ToAnimatedZero for Box<T>
-where
- T: ToAnimatedZero,
-{
- #[inline]
- fn to_animated_zero(&self) -> Result<Self, ()> {
- Ok(Box::new((**self).to_animated_zero()?))
- }
-}
-
-impl<T> ToAnimatedZero for Option<T>
-where
- T: ToAnimatedZero,
-{
- #[inline]
- fn to_animated_zero(&self) -> Result<Self, ()> {
- match *self {
- Some(ref value) => Ok(Some(value.to_animated_zero()?)),
- None => Ok(None),
- }
- }
-}
-
-impl<T> ToAnimatedZero for Vec<T>
-where
- T: ToAnimatedZero,
-{
- #[inline]
- fn to_animated_zero(&self) -> Result<Self, ()> {
- self.iter().map(|v| v.to_animated_zero()).collect()
- }
-}
-
-impl<T> ToAnimatedZero for Box<[T]>
-where
- T: ToAnimatedZero,
-{
- #[inline]
- fn to_animated_zero(&self) -> Result<Self, ()> {
- self.iter().map(|v| v.to_animated_zero()).collect()
- }
-}
-
-impl<T> ToAnimatedZero for crate::OwnedSlice<T>
-where
- T: ToAnimatedZero,
-{
- #[inline]
- fn to_animated_zero(&self) -> Result<Self, ()> {
- self.iter().map(|v| v.to_animated_zero()).collect()
- }
-}
-
-impl<T> ToAnimatedZero for crate::ArcSlice<T>
-where
- T: ToAnimatedZero,
-{
- #[inline]
- fn to_animated_zero(&self) -> Result<Self, ()> {
- let v = self
- .iter()
- .map(|v| v.to_animated_zero())
- .collect::<Result<Vec<_>, _>>()?;
- Ok(crate::ArcSlice::from_iter(v.into_iter()))
- }
-}
diff --git a/components/style/values/animated/svg.rs b/components/style/values/animated/svg.rs
deleted file mode 100644
index 04e35098adb..00000000000
--- a/components/style/values/animated/svg.rs
+++ /dev/null
@@ -1,46 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
-
-//! Animation implementations for various SVG-related types.
-
-use super::{Animate, Procedure};
-use crate::values::distance::{ComputeSquaredDistance, SquaredDistance};
-use crate::values::generics::svg::SVGStrokeDashArray;
-
-/// <https://www.w3.org/TR/SVG11/painting.html#StrokeDasharrayProperty>
-impl<L> Animate for SVGStrokeDashArray<L>
-where
- L: Clone + Animate,
-{
- #[inline]
- fn animate(&self, other: &Self, procedure: Procedure) -> Result<Self, ()> {
- if matches!(procedure, Procedure::Add | Procedure::Accumulate { .. }) {
- // Non-additive.
- return Err(());
- }
- match (self, other) {
- (&SVGStrokeDashArray::Values(ref this), &SVGStrokeDashArray::Values(ref other)) => {
- Ok(SVGStrokeDashArray::Values(
- super::lists::repeatable_list::animate(this, other, procedure)?,
- ))
- },
- _ => Err(()),
- }
- }
-}
-
-impl<L> ComputeSquaredDistance for SVGStrokeDashArray<L>
-where
- L: ComputeSquaredDistance,
-{
- #[inline]
- fn compute_squared_distance(&self, other: &Self) -> Result<SquaredDistance, ()> {
- match (self, other) {
- (&SVGStrokeDashArray::Values(ref this), &SVGStrokeDashArray::Values(ref other)) => {
- super::lists::repeatable_list::squared_distance(this, other)
- },
- _ => Err(()),
- }
- }
-}
diff --git a/components/style/values/animated/transform.rs b/components/style/values/animated/transform.rs
deleted file mode 100644
index d30f8b74f80..00000000000
--- a/components/style/values/animated/transform.rs
+++ /dev/null
@@ -1,1473 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
-
-//! Animated types for transform.
-// There are still some implementation on Matrix3D in animated_properties.mako.rs
-// because they still need mako to generate the code.
-
-use super::animate_multiplicative_factor;
-use super::{Animate, Procedure, ToAnimatedZero};
-use crate::values::computed::transform::Rotate as ComputedRotate;
-use crate::values::computed::transform::Scale as ComputedScale;
-use crate::values::computed::transform::Transform as ComputedTransform;
-use crate::values::computed::transform::TransformOperation as ComputedTransformOperation;
-use crate::values::computed::transform::Translate as ComputedTranslate;
-use crate::values::computed::transform::{DirectionVector, Matrix, Matrix3D};
-use crate::values::computed::Angle;
-use crate::values::computed::{Length, LengthPercentage};
-use crate::values::computed::{Number, Percentage};
-use crate::values::distance::{ComputeSquaredDistance, SquaredDistance};
-use crate::values::generics::transform::{self, Transform, TransformOperation};
-use crate::values::generics::transform::{Rotate, Scale, Translate};
-use crate::values::CSSFloat;
-use crate::Zero;
-use std::cmp;
-
-// ------------------------------------
-// Animations for Matrix/Matrix3D.
-// ------------------------------------
-/// A 2d matrix for interpolation.
-#[derive(Clone, ComputeSquaredDistance, Copy, Debug)]
-#[cfg_attr(feature = "servo", derive(MallocSizeOf))]
-#[allow(missing_docs)]
-// FIXME: We use custom derive for ComputeSquaredDistance. However, If possible, we should convert
-// the InnerMatrix2D into types with physical meaning. This custom derive computes the squared
-// distance from each matrix item, and this makes the result different from that in Gecko if we
-// have skew factor in the Matrix3D.
-pub struct InnerMatrix2D {
- pub m11: CSSFloat,
- pub m12: CSSFloat,
- pub m21: CSSFloat,
- pub m22: CSSFloat,
-}
-
-impl Animate for InnerMatrix2D {
- fn animate(&self, other: &Self, procedure: Procedure) -> Result<Self, ()> {
- Ok(InnerMatrix2D {
- m11: animate_multiplicative_factor(self.m11, other.m11, procedure)?,
- m12: self.m12.animate(&other.m12, procedure)?,
- m21: self.m21.animate(&other.m21, procedure)?,
- m22: animate_multiplicative_factor(self.m22, other.m22, procedure)?,
- })
- }
-}
-
-/// A 2d translation function.
-#[cfg_attr(feature = "servo", derive(MallocSizeOf))]
-#[derive(Animate, Clone, ComputeSquaredDistance, Copy, Debug)]
-pub struct Translate2D(f32, f32);
-
-/// A 2d scale function.
-#[derive(Clone, ComputeSquaredDistance, Copy, Debug)]
-#[cfg_attr(feature = "servo", derive(MallocSizeOf))]
-pub struct Scale2D(f32, f32);
-
-impl Animate for Scale2D {
- fn animate(&self, other: &Self, procedure: Procedure) -> Result<Self, ()> {
- Ok(Scale2D(
- animate_multiplicative_factor(self.0, other.0, procedure)?,
- animate_multiplicative_factor(self.1, other.1, procedure)?,
- ))
- }
-}
-
-/// A decomposed 2d matrix.
-#[derive(Clone, Copy, Debug)]
-#[cfg_attr(feature = "servo", derive(MallocSizeOf))]
-pub struct MatrixDecomposed2D {
- /// The translation function.
- pub translate: Translate2D,
- /// The scale function.
- pub scale: Scale2D,
- /// The rotation angle.
- pub angle: f32,
- /// The inner matrix.
- pub matrix: InnerMatrix2D,
-}
-
-impl Animate for MatrixDecomposed2D {
- /// <https://drafts.csswg.org/css-transforms/#interpolation-of-decomposed-2d-matrix-values>
- fn animate(&self, other: &Self, procedure: Procedure) -> Result<Self, ()> {
- // If x-axis of one is flipped, and y-axis of the other,
- // convert to an unflipped rotation.
- let mut scale = self.scale;
- let mut angle = self.angle;
- let mut other_angle = other.angle;
- if (scale.0 < 0.0 && other.scale.1 < 0.0) || (scale.1 < 0.0 && other.scale.0 < 0.0) {
- scale.0 = -scale.0;
- scale.1 = -scale.1;
- angle += if angle < 0.0 { 180. } else { -180. };
- }
-
- // Don't rotate the long way around.
- if angle == 0.0 {
- angle = 360.
- }
- if other_angle == 0.0 {
- other_angle = 360.
- }
-
- if (angle - other_angle).abs() > 180. {
- if angle > other_angle {
- angle -= 360.
- } else {
- other_angle -= 360.
- }
- }
-
- // Interpolate all values.
- let translate = self.translate.animate(&other.translate, procedure)?;
- let scale = scale.animate(&other.scale, procedure)?;
- let angle = angle.animate(&other_angle, procedure)?;
- let matrix = self.matrix.animate(&other.matrix, procedure)?;
-
- Ok(MatrixDecomposed2D {
- translate,
- scale,
- angle,
- matrix,
- })
- }
-}
-
-impl ComputeSquaredDistance for MatrixDecomposed2D {
- #[inline]
- fn compute_squared_distance(&self, other: &Self) -> Result<SquaredDistance, ()> {
- // Use Radian to compute the distance.
- const RAD_PER_DEG: f64 = std::f64::consts::PI / 180.0;
- let angle1 = self.angle as f64 * RAD_PER_DEG;
- let angle2 = other.angle as f64 * RAD_PER_DEG;
- Ok(self.translate.compute_squared_distance(&other.translate)? +
- self.scale.compute_squared_distance(&other.scale)? +
- angle1.compute_squared_distance(&angle2)? +
- self.matrix.compute_squared_distance(&other.matrix)?)
- }
-}
-
-impl From<Matrix3D> for MatrixDecomposed2D {
- /// Decompose a 2D matrix.
- /// <https://drafts.csswg.org/css-transforms/#decomposing-a-2d-matrix>
- fn from(matrix: Matrix3D) -> MatrixDecomposed2D {
- let mut row0x = matrix.m11;
- let mut row0y = matrix.m12;
- let mut row1x = matrix.m21;
- let mut row1y = matrix.m22;
-
- let translate = Translate2D(matrix.m41, matrix.m42);
- let mut scale = Scale2D(
- (row0x * row0x + row0y * row0y).sqrt(),
- (row1x * row1x + row1y * row1y).sqrt(),
- );
-
- // If determinant is negative, one axis was flipped.
- let determinant = row0x * row1y - row0y * row1x;
- if determinant < 0. {
- if row0x < row1y {
- scale.0 = -scale.0;
- } else {
- scale.1 = -scale.1;
- }
- }
-
- // Renormalize matrix to remove scale.
- if scale.0 != 0.0 {
- row0x *= 1. / scale.0;
- row0y *= 1. / scale.0;
- }
- if scale.1 != 0.0 {
- row1x *= 1. / scale.1;
- row1y *= 1. / scale.1;
- }
-
- // Compute rotation and renormalize matrix.
- let mut angle = row0y.atan2(row0x);
- if angle != 0.0 {
- let sn = -row0y;
- let cs = row0x;
- let m11 = row0x;
- let m12 = row0y;
- let m21 = row1x;
- let m22 = row1y;
- row0x = cs * m11 + sn * m21;
- row0y = cs * m12 + sn * m22;
- row1x = -sn * m11 + cs * m21;
- row1y = -sn * m12 + cs * m22;
- }
-
- let m = InnerMatrix2D {
- m11: row0x,
- m12: row0y,
- m21: row1x,
- m22: row1y,
- };
-
- // Convert into degrees because our rotation functions expect it.
- angle = angle.to_degrees();
- MatrixDecomposed2D {
- translate: translate,
- scale: scale,
- angle: angle,
- matrix: m,
- }
- }
-}
-
-impl From<MatrixDecomposed2D> for Matrix3D {
- /// Recompose a 2D matrix.
- /// <https://drafts.csswg.org/css-transforms/#recomposing-to-a-2d-matrix>
- fn from(decomposed: MatrixDecomposed2D) -> Matrix3D {
- let mut computed_matrix = Matrix3D::identity();
- computed_matrix.m11 = decomposed.matrix.m11;
- computed_matrix.m12 = decomposed.matrix.m12;
- computed_matrix.m21 = decomposed.matrix.m21;
- computed_matrix.m22 = decomposed.matrix.m22;
-
- // Translate matrix.
- computed_matrix.m41 = decomposed.translate.0;
- computed_matrix.m42 = decomposed.translate.1;
-
- // Rotate matrix.
- let angle = decomposed.angle.to_radians();
- let cos_angle = angle.cos();
- let sin_angle = angle.sin();
-
- let mut rotate_matrix = Matrix3D::identity();
- rotate_matrix.m11 = cos_angle;
- rotate_matrix.m12 = sin_angle;
- rotate_matrix.m21 = -sin_angle;
- rotate_matrix.m22 = cos_angle;
-
- // Multiplication of computed_matrix and rotate_matrix
- computed_matrix = rotate_matrix.multiply(&computed_matrix);
-
- // Scale matrix.
- computed_matrix.m11 *= decomposed.scale.0;
- computed_matrix.m12 *= decomposed.scale.0;
- computed_matrix.m21 *= decomposed.scale.1;
- computed_matrix.m22 *= decomposed.scale.1;
- computed_matrix
- }
-}
-
-impl Animate for Matrix {
- #[cfg(feature = "servo")]
- fn animate(&self, other: &Self, procedure: Procedure) -> Result<Self, ()> {
- let this = Matrix3D::from(*self);
- let other = Matrix3D::from(*other);
- let this = MatrixDecomposed2D::from(this);
- let other = MatrixDecomposed2D::from(other);
- Matrix3D::from(this.animate(&other, procedure)?).into_2d()
- }
-
- #[cfg(feature = "gecko")]
- // Gecko doesn't exactly follow the spec here; we use a different procedure
- // to match it
- fn animate(&self, other: &Self, procedure: Procedure) -> Result<Self, ()> {
- let this = Matrix3D::from(*self);
- let other = Matrix3D::from(*other);
- let from = decompose_2d_matrix(&this)?;
- let to = decompose_2d_matrix(&other)?;
- Matrix3D::from(from.animate(&to, procedure)?).into_2d()
- }
-}
-
-/// A 3d translation.
-#[cfg_attr(feature = "servo", derive(MallocSizeOf))]
-#[derive(Animate, Clone, ComputeSquaredDistance, Copy, Debug)]
-pub struct Translate3D(pub f32, pub f32, pub f32);
-
-/// A 3d scale function.
-#[derive(Clone, ComputeSquaredDistance, Copy, Debug)]
-#[cfg_attr(feature = "servo", derive(MallocSizeOf))]
-pub struct Scale3D(pub f32, pub f32, pub f32);
-
-impl Scale3D {
- /// Negate self.
- fn negate(&mut self) {
- self.0 *= -1.0;
- self.1 *= -1.0;
- self.2 *= -1.0;
- }
-}
-
-impl Animate for Scale3D {
- fn animate(&self, other: &Self, procedure: Procedure) -> Result<Self, ()> {
- Ok(Scale3D(
- animate_multiplicative_factor(self.0, other.0, procedure)?,
- animate_multiplicative_factor(self.1, other.1, procedure)?,
- animate_multiplicative_factor(self.2, other.2, procedure)?,
- ))
- }
-}
-
-/// A 3d skew function.
-#[cfg_attr(feature = "servo", derive(MallocSizeOf))]
-#[derive(Animate, Clone, Copy, Debug)]
-pub struct Skew(f32, f32, f32);
-
-impl ComputeSquaredDistance for Skew {
- // We have to use atan() to convert the skew factors into skew angles, so implement
- // ComputeSquaredDistance manually.
- #[inline]
- fn compute_squared_distance(&self, other: &Self) -> Result<SquaredDistance, ()> {
- Ok(self.0.atan().compute_squared_distance(&other.0.atan())? +
- self.1.atan().compute_squared_distance(&other.1.atan())? +
- self.2.atan().compute_squared_distance(&other.2.atan())?)
- }
-}
-
-/// A 3d perspective transformation.
-#[derive(Clone, ComputeSquaredDistance, Copy, Debug)]
-#[cfg_attr(feature = "servo", derive(MallocSizeOf))]
-pub struct Perspective(pub f32, pub f32, pub f32, pub f32);
-
-impl Animate for Perspective {
- fn animate(&self, other: &Self, procedure: Procedure) -> Result<Self, ()> {
- Ok(Perspective(
- self.0.animate(&other.0, procedure)?,
- self.1.animate(&other.1, procedure)?,
- self.2.animate(&other.2, procedure)?,
- animate_multiplicative_factor(self.3, other.3, procedure)?,
- ))
- }
-}
-
-/// A quaternion used to represent a rotation.
-#[derive(Clone, Copy, Debug)]
-#[cfg_attr(feature = "servo", derive(MallocSizeOf))]
-pub struct Quaternion(f64, f64, f64, f64);
-
-impl Quaternion {
- /// Return a quaternion from a unit direction vector and angle (unit: radian).
- #[inline]
- fn from_direction_and_angle(vector: &DirectionVector, angle: f64) -> Self {
- debug_assert!(
- (vector.length() - 1.).abs() < 0.0001,
- "Only accept an unit direction vector to create a quaternion"
- );
- // Reference:
- // https://en.wikipedia.org/wiki/Quaternions_and_spatial_rotation
- //
- // if the direction axis is (x, y, z) = xi + yj + zk,
- // and the angle is |theta|, this formula can be done using
- // an extension of Euler's formula:
- // q = cos(theta/2) + (xi + yj + zk)(sin(theta/2))
- // = cos(theta/2) +
- // x*sin(theta/2)i + y*sin(theta/2)j + z*sin(theta/2)k
- Quaternion(
- vector.x as f64 * (angle / 2.).sin(),
- vector.y as f64 * (angle / 2.).sin(),
- vector.z as f64 * (angle / 2.).sin(),
- (angle / 2.).cos(),
- )
- }
-
- /// Calculate the dot product.
- #[inline]
- fn dot(&self, other: &Self) -> f64 {
- self.0 * other.0 + self.1 * other.1 + self.2 * other.2 + self.3 * other.3
- }
-
- /// Return the scaled quaternion by a factor.
- #[inline]
- fn scale(&self, factor: f64) -> Self {
- Quaternion(
- self.0 * factor,
- self.1 * factor,
- self.2 * factor,
- self.3 * factor,
- )
- }
-}
-
-impl Animate for Quaternion {
- fn animate(&self, other: &Self, procedure: Procedure) -> Result<Self, ()> {
- let (this_weight, other_weight) = procedure.weights();
- debug_assert!(
- // Doule EPSILON since both this_weight and other_weght have calculation errors
- // which are approximately equal to EPSILON.
- (this_weight + other_weight - 1.0f64).abs() <= f64::EPSILON * 2.0 ||
- other_weight == 1.0f64 ||
- other_weight == 0.0f64,
- "animate should only be used for interpolating or accumulating transforms"
- );
-
- // We take a specialized code path for accumulation (where other_weight
- // is 1).
- if let Procedure::Accumulate { .. } = procedure {
- debug_assert_eq!(other_weight, 1.0);
- if this_weight == 0.0 {
- return Ok(*other);
- }
-
- let clamped_w = self.3.min(1.0).max(-1.0);
-
- // Determine the scale factor.
- let mut theta = clamped_w.acos();
- let mut scale = if theta == 0.0 { 0.0 } else { 1.0 / theta.sin() };
- theta *= this_weight;
- scale *= theta.sin();
-
- // Scale the self matrix by this_weight.
- let mut scaled_self = *self;
- scaled_self.0 *= scale;
- scaled_self.1 *= scale;
- scaled_self.2 *= scale;
- scaled_self.3 = theta.cos();
-
- // Multiply scaled-self by other.
- let a = &scaled_self;
- let b = other;
- return Ok(Quaternion(
- a.3 * b.0 + a.0 * b.3 + a.1 * b.2 - a.2 * b.1,
- a.3 * b.1 - a.0 * b.2 + a.1 * b.3 + a.2 * b.0,
- a.3 * b.2 + a.0 * b.1 - a.1 * b.0 + a.2 * b.3,
- a.3 * b.3 - a.0 * b.0 - a.1 * b.1 - a.2 * b.2,
- ));
- }
-
- // Straight from gfxQuaternion::Slerp.
- //
- // Dot product, clamped between -1 and 1.
- let dot = (self.0 * other.0 + self.1 * other.1 + self.2 * other.2 + self.3 * other.3)
- .min(1.0)
- .max(-1.0);
-
- if dot.abs() == 1.0 {
- return Ok(*self);
- }
-
- let theta = dot.acos();
- let rsintheta = 1.0 / (1.0 - dot * dot).sqrt();
-
- let right_weight = (other_weight * theta).sin() * rsintheta;
- let left_weight = (other_weight * theta).cos() - dot * right_weight;
-
- let left = self.scale(left_weight);
- let right = other.scale(right_weight);
-
- Ok(Quaternion(
- left.0 + right.0,
- left.1 + right.1,
- left.2 + right.2,
- left.3 + right.3,
- ))
- }
-}
-
-impl ComputeSquaredDistance for Quaternion {
- #[inline]
- fn compute_squared_distance(&self, other: &Self) -> Result<SquaredDistance, ()> {
- // Use quaternion vectors to get the angle difference. Both q1 and q2 are unit vectors,
- // so we can get their angle difference by:
- // cos(theta/2) = (q1 dot q2) / (|q1| * |q2|) = q1 dot q2.
- let distance = self.dot(other).max(-1.0).min(1.0).acos() * 2.0;
- Ok(SquaredDistance::from_sqrt(distance))
- }
-}
-
-/// A decomposed 3d matrix.
-#[derive(Animate, Clone, ComputeSquaredDistance, Copy, Debug)]
-#[cfg_attr(feature = "servo", derive(MallocSizeOf))]
-pub struct MatrixDecomposed3D {
- /// A translation function.
- pub translate: Translate3D,
- /// A scale function.
- pub scale: Scale3D,
- /// The skew component of the transformation.
- pub skew: Skew,
- /// The perspective component of the transformation.
- pub perspective: Perspective,
- /// The quaternion used to represent the rotation.
- pub quaternion: Quaternion,
-}
-
-impl From<MatrixDecomposed3D> for Matrix3D {
- /// Recompose a 3D matrix.
- /// <https://drafts.csswg.org/css-transforms/#recomposing-to-a-3d-matrix>
- fn from(decomposed: MatrixDecomposed3D) -> Matrix3D {
- let mut matrix = Matrix3D::identity();
-
- // Apply perspective
- matrix.set_perspective(&decomposed.perspective);
-
- // Apply translation
- matrix.apply_translate(&decomposed.translate);
-
- // Apply rotation
- {
- let x = decomposed.quaternion.0;
- let y = decomposed.quaternion.1;
- let z = decomposed.quaternion.2;
- let w = decomposed.quaternion.3;
-
- // Construct a composite rotation matrix from the quaternion values
- // rotationMatrix is a identity 4x4 matrix initially
- let mut rotation_matrix = Matrix3D::identity();
- rotation_matrix.m11 = 1.0 - 2.0 * (y * y + z * z) as f32;
- rotation_matrix.m12 = 2.0 * (x * y + z * w) as f32;
- rotation_matrix.m13 = 2.0 * (x * z - y * w) as f32;
- rotation_matrix.m21 = 2.0 * (x * y - z * w) as f32;
- rotation_matrix.m22 = 1.0 - 2.0 * (x * x + z * z) as f32;
- rotation_matrix.m23 = 2.0 * (y * z + x * w) as f32;
- rotation_matrix.m31 = 2.0 * (x * z + y * w) as f32;
- rotation_matrix.m32 = 2.0 * (y * z - x * w) as f32;
- rotation_matrix.m33 = 1.0 - 2.0 * (x * x + y * y) as f32;
-
- matrix = rotation_matrix.multiply(&matrix);
- }
-
- // Apply skew
- {
- let mut temp = Matrix3D::identity();
- if decomposed.skew.2 != 0.0 {
- temp.m32 = decomposed.skew.2;
- matrix = temp.multiply(&matrix);
- temp.m32 = 0.0;
- }
-
- if decomposed.skew.1 != 0.0 {
- temp.m31 = decomposed.skew.1;
- matrix = temp.multiply(&matrix);
- temp.m31 = 0.0;
- }
-
- if decomposed.skew.0 != 0.0 {
- temp.m21 = decomposed.skew.0;
- matrix = temp.multiply(&matrix);
- }
- }
-
- // Apply scale
- matrix.apply_scale(&decomposed.scale);
-
- matrix
- }
-}
-
-/// Decompose a 3D matrix.
-/// https://drafts.csswg.org/css-transforms-2/#decomposing-a-3d-matrix
-/// http://www.realtimerendering.com/resources/GraphicsGems/gemsii/unmatrix.c
-fn decompose_3d_matrix(mut matrix: Matrix3D) -> Result<MatrixDecomposed3D, ()> {
- // Combine 2 point.
- let combine = |a: [f32; 3], b: [f32; 3], ascl: f32, bscl: f32| {
- [
- (ascl * a[0]) + (bscl * b[0]),
- (ascl * a[1]) + (bscl * b[1]),
- (ascl * a[2]) + (bscl * b[2]),
- ]
- };
- // Dot product.
- let dot = |a: [f32; 3], b: [f32; 3]| a[0] * b[0] + a[1] * b[1] + a[2] * b[2];
- // Cross product.
- let cross = |row1: [f32; 3], row2: [f32; 3]| {
- [
- row1[1] * row2[2] - row1[2] * row2[1],
- row1[2] * row2[0] - row1[0] * row2[2],
- row1[0] * row2[1] - row1[1] * row2[0],
- ]
- };
-
- if matrix.m44 == 0.0 {
- return Err(());
- }
-
- let scaling_factor = matrix.m44;
-
- // Normalize the matrix.
- matrix.scale_by_factor(1.0 / scaling_factor);
-
- // perspective_matrix is used to solve for perspective, but it also provides
- // an easy way to test for singularity of the upper 3x3 component.
- let mut perspective_matrix = matrix;
-
- perspective_matrix.m14 = 0.0;
- perspective_matrix.m24 = 0.0;
- perspective_matrix.m34 = 0.0;
- perspective_matrix.m44 = 1.0;
-
- if perspective_matrix.determinant() == 0.0 {
- return Err(());
- }
-
- // First, isolate perspective.
- let perspective = if matrix.m14 != 0.0 || matrix.m24 != 0.0 || matrix.m34 != 0.0 {
- let right_hand_side: [f32; 4] = [matrix.m14, matrix.m24, matrix.m34, matrix.m44];
-
- perspective_matrix = perspective_matrix.inverse().unwrap().transpose();
- let perspective = perspective_matrix.pre_mul_point4(&right_hand_side);
- // NOTE(emilio): Even though the reference algorithm clears the
- // fourth column here (matrix.m14..matrix.m44), they're not used below
- // so it's not really needed.
- Perspective(
- perspective[0],
- perspective[1],
- perspective[2],
- perspective[3],
- )
- } else {
- Perspective(0.0, 0.0, 0.0, 1.0)
- };
-
- // Next take care of translation (easy).
- let translate = Translate3D(matrix.m41, matrix.m42, matrix.m43);
-
- // Now get scale and shear. 'row' is a 3 element array of 3 component vectors
- let mut row = matrix.get_matrix_3x3_part();
-
- // Compute X scale factor and normalize first row.
- let row0len = (row[0][0] * row[0][0] + row[0][1] * row[0][1] + row[0][2] * row[0][2]).sqrt();
- let mut scale = Scale3D(row0len, 0.0, 0.0);
- row[0] = [
- row[0][0] / row0len,
- row[0][1] / row0len,
- row[0][2] / row0len,
- ];
-
- // Compute XY shear factor and make 2nd row orthogonal to 1st.
- let mut skew = Skew(dot(row[0], row[1]), 0.0, 0.0);
- row[1] = combine(row[1], row[0], 1.0, -skew.0);
-
- // Now, compute Y scale and normalize 2nd row.
- let row1len = (row[1][0] * row[1][0] + row[1][1] * row[1][1] + row[1][2] * row[1][2]).sqrt();
- scale.1 = row1len;
- row[1] = [
- row[1][0] / row1len,
- row[1][1] / row1len,
- row[1][2] / row1len,
- ];
- skew.0 /= scale.1;
-
- // Compute XZ and YZ shears, orthogonalize 3rd row
- skew.1 = dot(row[0], row[2]);
- row[2] = combine(row[2], row[0], 1.0, -skew.1);
- skew.2 = dot(row[1], row[2]);
- row[2] = combine(row[2], row[1], 1.0, -skew.2);
-
- // Next, get Z scale and normalize 3rd row.
- let row2len = (row[2][0] * row[2][0] + row[2][1] * row[2][1] + row[2][2] * row[2][2]).sqrt();
- scale.2 = row2len;
- row[2] = [
- row[2][0] / row2len,
- row[2][1] / row2len,
- row[2][2] / row2len,
- ];
- skew.1 /= scale.2;
- skew.2 /= scale.2;
-
- // At this point, the matrix (in rows) is orthonormal.
- // Check for a coordinate system flip. If the determinant
- // is -1, then negate the matrix and the scaling factors.
- if dot(row[0], cross(row[1], row[2])) < 0.0 {
- scale.negate();
- for i in 0..3 {
- row[i][0] *= -1.0;
- row[i][1] *= -1.0;
- row[i][2] *= -1.0;
- }
- }
-
- // Now, get the rotations out.
- let mut quaternion = Quaternion(
- 0.5 * ((1.0 + row[0][0] - row[1][1] - row[2][2]).max(0.0) as f64).sqrt(),
- 0.5 * ((1.0 - row[0][0] + row[1][1] - row[2][2]).max(0.0) as f64).sqrt(),
- 0.5 * ((1.0 - row[0][0] - row[1][1] + row[2][2]).max(0.0) as f64).sqrt(),
- 0.5 * ((1.0 + row[0][0] + row[1][1] + row[2][2]).max(0.0) as f64).sqrt(),
- );
-
- if row[2][1] > row[1][2] {
- quaternion.0 = -quaternion.0
- }
- if row[0][2] > row[2][0] {
- quaternion.1 = -quaternion.1
- }
- if row[1][0] > row[0][1] {
- quaternion.2 = -quaternion.2
- }
-
- Ok(MatrixDecomposed3D {
- translate,
- scale,
- skew,
- perspective,
- quaternion,
- })
-}
-
-/// Decompose a 2D matrix for Gecko.
-// Use the algorithm from nsStyleTransformMatrix::Decompose2DMatrix() in Gecko.
-#[cfg(feature = "gecko")]
-fn decompose_2d_matrix(matrix: &Matrix3D) -> Result<MatrixDecomposed3D, ()> {
- // The index is column-major, so the equivalent transform matrix is:
- // | m11 m21 0 m41 | => | m11 m21 | and translate(m41, m42)
- // | m12 m22 0 m42 | | m12 m22 |
- // | 0 0 1 0 |
- // | 0 0 0 1 |
- let (mut m11, mut m12) = (matrix.m11, matrix.m12);
- let (mut m21, mut m22) = (matrix.m21, matrix.m22);
- // Check if this is a singular matrix.
- if m11 * m22 == m12 * m21 {
- return Err(());
- }
-
- let mut scale_x = (m11 * m11 + m12 * m12).sqrt();
- m11 /= scale_x;
- m12 /= scale_x;
-
- let mut shear_xy = m11 * m21 + m12 * m22;
- m21 -= m11 * shear_xy;
- m22 -= m12 * shear_xy;
-
- let scale_y = (m21 * m21 + m22 * m22).sqrt();
- m21 /= scale_y;
- m22 /= scale_y;
- shear_xy /= scale_y;
-
- let determinant = m11 * m22 - m12 * m21;
- // Determinant should now be 1 or -1.
- if 0.99 > determinant.abs() || determinant.abs() > 1.01 {
- return Err(());
- }
-
- if determinant < 0. {
- m11 = -m11;
- m12 = -m12;
- shear_xy = -shear_xy;
- scale_x = -scale_x;
- }
-
- Ok(MatrixDecomposed3D {
- translate: Translate3D(matrix.m41, matrix.m42, 0.),
- scale: Scale3D(scale_x, scale_y, 1.),
- skew: Skew(shear_xy, 0., 0.),
- perspective: Perspective(0., 0., 0., 1.),
- quaternion: Quaternion::from_direction_and_angle(
- &DirectionVector::new(0., 0., 1.),
- m12.atan2(m11) as f64,
- ),
- })
-}
-
-impl Animate for Matrix3D {
- #[cfg(feature = "servo")]
- fn animate(&self, other: &Self, procedure: Procedure) -> Result<Self, ()> {
- if self.is_3d() || other.is_3d() {
- let decomposed_from = decompose_3d_matrix(*self);
- let decomposed_to = decompose_3d_matrix(*other);
- match (decomposed_from, decomposed_to) {
- (Ok(this), Ok(other)) => Ok(Matrix3D::from(this.animate(&other, procedure)?)),
- // Matrices can be undecomposable due to couple reasons, e.g.,
- // non-invertible matrices. In this case, we should report Err
- // here, and let the caller do the fallback procedure.
- _ => Err(()),
- }
- } else {
- let this = MatrixDecomposed2D::from(*self);
- let other = MatrixDecomposed2D::from(*other);
- Ok(Matrix3D::from(this.animate(&other, procedure)?))
- }
- }
-
- #[cfg(feature = "gecko")]
- // Gecko doesn't exactly follow the spec here; we use a different procedure
- // to match it
- fn animate(&self, other: &Self, procedure: Procedure) -> Result<Self, ()> {
- let (from, to) = if self.is_3d() || other.is_3d() {
- (decompose_3d_matrix(*self)?, decompose_3d_matrix(*other)?)
- } else {
- (decompose_2d_matrix(self)?, decompose_2d_matrix(other)?)
- };
- // Matrices can be undecomposable due to couple reasons, e.g.,
- // non-invertible matrices. In this case, we should report Err here,
- // and let the caller do the fallback procedure.
- Ok(Matrix3D::from(from.animate(&to, procedure)?))
- }
-}
-
-impl ComputeSquaredDistance for Matrix3D {
- #[inline]
- #[cfg(feature = "servo")]
- fn compute_squared_distance(&self, other: &Self) -> Result<SquaredDistance, ()> {
- if self.is_3d() || other.is_3d() {
- let from = decompose_3d_matrix(*self)?;
- let to = decompose_3d_matrix(*other)?;
- from.compute_squared_distance(&to)
- } else {
- let from = MatrixDecomposed2D::from(*self);
- let to = MatrixDecomposed2D::from(*other);
- from.compute_squared_distance(&to)
- }
- }
-
- #[inline]
- #[cfg(feature = "gecko")]
- fn compute_squared_distance(&self, other: &Self) -> Result<SquaredDistance, ()> {
- let (from, to) = if self.is_3d() || other.is_3d() {
- (decompose_3d_matrix(*self)?, decompose_3d_matrix(*other)?)
- } else {
- (decompose_2d_matrix(self)?, decompose_2d_matrix(other)?)
- };
- from.compute_squared_distance(&to)
- }
-}
-
-// ------------------------------------
-// Animation for Transform list.
-// ------------------------------------
-fn is_matched_operation(
- first: &ComputedTransformOperation,
- second: &ComputedTransformOperation,
-) -> bool {
- match (first, second) {
- (&TransformOperation::Matrix(..), &TransformOperation::Matrix(..)) |
- (&TransformOperation::Matrix3D(..), &TransformOperation::Matrix3D(..)) |
- (&TransformOperation::Skew(..), &TransformOperation::Skew(..)) |
- (&TransformOperation::SkewX(..), &TransformOperation::SkewX(..)) |
- (&TransformOperation::SkewY(..), &TransformOperation::SkewY(..)) |
- (&TransformOperation::Rotate(..), &TransformOperation::Rotate(..)) |
- (&TransformOperation::Rotate3D(..), &TransformOperation::Rotate3D(..)) |
- (&TransformOperation::RotateX(..), &TransformOperation::RotateX(..)) |
- (&TransformOperation::RotateY(..), &TransformOperation::RotateY(..)) |
- (&TransformOperation::RotateZ(..), &TransformOperation::RotateZ(..)) |
- (&TransformOperation::Perspective(..), &TransformOperation::Perspective(..)) => true,
- // Match functions that have the same primitive transform function
- (a, b) if a.is_translate() && b.is_translate() => true,
- (a, b) if a.is_scale() && b.is_scale() => true,
- (a, b) if a.is_rotate() && b.is_rotate() => true,
- // InterpolateMatrix and AccumulateMatrix are for mismatched transforms
- _ => false,
- }
-}
-
-/// <https://drafts.csswg.org/css-transforms/#interpolation-of-transforms>
-impl Animate for ComputedTransform {
- #[inline]
- fn animate(&self, other: &Self, procedure: Procedure) -> Result<Self, ()> {
- use std::borrow::Cow;
-
- // Addition for transforms simply means appending to the list of
- // transform functions. This is different to how we handle the other
- // animation procedures so we treat it separately here rather than
- // handling it in TransformOperation.
- if procedure == Procedure::Add {
- let result = self.0.iter().chain(&*other.0).cloned().collect();
- return Ok(Transform(result));
- }
-
- let this = Cow::Borrowed(&self.0);
- let other = Cow::Borrowed(&other.0);
-
- // Interpolate the common prefix
- let mut result = this
- .iter()
- .zip(other.iter())
- .take_while(|(this, other)| is_matched_operation(this, other))
- .map(|(this, other)| this.animate(other, procedure))
- .collect::<Result<Vec<_>, _>>()?;
-
- // Deal with the remainders
- let this_remainder = if this.len() > result.len() {
- Some(&this[result.len()..])
- } else {
- None
- };
- let other_remainder = if other.len() > result.len() {
- Some(&other[result.len()..])
- } else {
- None
- };
-
- match (this_remainder, other_remainder) {
- // If there is a remainder from *both* lists we must have had mismatched functions.
- // => Add the remainders to a suitable ___Matrix function.
- (Some(this_remainder), Some(other_remainder)) => {
- result.push(TransformOperation::animate_mismatched_transforms(
- this_remainder,
- other_remainder,
- procedure,
- )?);
- },
- // If there is a remainder from just one list, then one list must be shorter but
- // completely match the type of the corresponding functions in the longer list.
- // => Interpolate the remainder with identity transforms.
- (Some(remainder), None) | (None, Some(remainder)) => {
- let fill_right = this_remainder.is_some();
- result.append(
- &mut remainder
- .iter()
- .map(|transform| {
- let identity = transform.to_animated_zero().unwrap();
-
- match transform {
- TransformOperation::AccumulateMatrix { .. } |
- TransformOperation::InterpolateMatrix { .. } => {
- let (from, to) = if fill_right {
- (transform, &identity)
- } else {
- (&identity, transform)
- };
-
- TransformOperation::animate_mismatched_transforms(
- &[from.clone()],
- &[to.clone()],
- procedure,
- )
- },
- _ => {
- let (lhs, rhs) = if fill_right {
- (transform, &identity)
- } else {
- (&identity, transform)
- };
- lhs.animate(rhs, procedure)
- },
- }
- })
- .collect::<Result<Vec<_>, _>>()?,
- );
- },
- (None, None) => {},
- }
-
- Ok(Transform(result.into()))
- }
-}
-
-impl ComputeSquaredDistance for ComputedTransform {
- #[inline]
- fn compute_squared_distance(&self, other: &Self) -> Result<SquaredDistance, ()> {
- let squared_dist = super::lists::with_zero::squared_distance(&self.0, &other.0);
-
- // Roll back to matrix interpolation if there is any Err(()) in the
- // transform lists, such as mismatched transform functions.
- //
- // FIXME: Using a zero size here seems a bit sketchy but matches the
- // previous behavior.
- if squared_dist.is_err() {
- let rect = euclid::Rect::zero();
- let matrix1: Matrix3D = self.to_transform_3d_matrix(Some(&rect))?.0.into();
- let matrix2: Matrix3D = other.to_transform_3d_matrix(Some(&rect))?.0.into();
- return matrix1.compute_squared_distance(&matrix2);
- }
-
- squared_dist
- }
-}
-
-/// <http://dev.w3.org/csswg/css-transforms/#interpolation-of-transforms>
-impl Animate for ComputedTransformOperation {
- fn animate(&self, other: &Self, procedure: Procedure) -> Result<Self, ()> {
- match (self, other) {
- (&TransformOperation::Matrix3D(ref this), &TransformOperation::Matrix3D(ref other)) => {
- Ok(TransformOperation::Matrix3D(
- this.animate(other, procedure)?,
- ))
- },
- (&TransformOperation::Matrix(ref this), &TransformOperation::Matrix(ref other)) => {
- Ok(TransformOperation::Matrix(this.animate(other, procedure)?))
- },
- (
- &TransformOperation::Skew(ref fx, ref fy),
- &TransformOperation::Skew(ref tx, ref ty),
- ) => Ok(TransformOperation::Skew(
- fx.animate(tx, procedure)?,
- fy.animate(ty, procedure)?,
- )),
- (&TransformOperation::SkewX(ref f), &TransformOperation::SkewX(ref t)) => {
- Ok(TransformOperation::SkewX(f.animate(t, procedure)?))
- },
- (&TransformOperation::SkewY(ref f), &TransformOperation::SkewY(ref t)) => {
- Ok(TransformOperation::SkewY(f.animate(t, procedure)?))
- },
- (
- &TransformOperation::Translate3D(ref fx, ref fy, ref fz),
- &TransformOperation::Translate3D(ref tx, ref ty, ref tz),
- ) => Ok(TransformOperation::Translate3D(
- fx.animate(tx, procedure)?,
- fy.animate(ty, procedure)?,
- fz.animate(tz, procedure)?,
- )),
- (
- &TransformOperation::Translate(ref fx, ref fy),
- &TransformOperation::Translate(ref tx, ref ty),
- ) => Ok(TransformOperation::Translate(
- fx.animate(tx, procedure)?,
- fy.animate(ty, procedure)?,
- )),
- (&TransformOperation::TranslateX(ref f), &TransformOperation::TranslateX(ref t)) => {
- Ok(TransformOperation::TranslateX(f.animate(t, procedure)?))
- },
- (&TransformOperation::TranslateY(ref f), &TransformOperation::TranslateY(ref t)) => {
- Ok(TransformOperation::TranslateY(f.animate(t, procedure)?))
- },
- (&TransformOperation::TranslateZ(ref f), &TransformOperation::TranslateZ(ref t)) => {
- Ok(TransformOperation::TranslateZ(f.animate(t, procedure)?))
- },
- (
- &TransformOperation::Scale3D(ref fx, ref fy, ref fz),
- &TransformOperation::Scale3D(ref tx, ref ty, ref tz),
- ) => Ok(TransformOperation::Scale3D(
- animate_multiplicative_factor(*fx, *tx, procedure)?,
- animate_multiplicative_factor(*fy, *ty, procedure)?,
- animate_multiplicative_factor(*fz, *tz, procedure)?,
- )),
- (&TransformOperation::ScaleX(ref f), &TransformOperation::ScaleX(ref t)) => Ok(
- TransformOperation::ScaleX(animate_multiplicative_factor(*f, *t, procedure)?),
- ),
- (&TransformOperation::ScaleY(ref f), &TransformOperation::ScaleY(ref t)) => Ok(
- TransformOperation::ScaleY(animate_multiplicative_factor(*f, *t, procedure)?),
- ),
- (&TransformOperation::ScaleZ(ref f), &TransformOperation::ScaleZ(ref t)) => Ok(
- TransformOperation::ScaleZ(animate_multiplicative_factor(*f, *t, procedure)?),
- ),
- (
- &TransformOperation::Scale(ref fx, ref fy),
- &TransformOperation::Scale(ref tx, ref ty),
- ) => Ok(TransformOperation::Scale(
- animate_multiplicative_factor(*fx, *tx, procedure)?,
- animate_multiplicative_factor(*fy, *ty, procedure)?,
- )),
- (
- &TransformOperation::Rotate3D(fx, fy, fz, fa),
- &TransformOperation::Rotate3D(tx, ty, tz, ta),
- ) => {
- let animated = Rotate::Rotate3D(fx, fy, fz, fa)
- .animate(&Rotate::Rotate3D(tx, ty, tz, ta), procedure)?;
- let (fx, fy, fz, fa) = ComputedRotate::resolve(&animated);
- Ok(TransformOperation::Rotate3D(fx, fy, fz, fa))
- },
- (&TransformOperation::RotateX(fa), &TransformOperation::RotateX(ta)) => {
- Ok(TransformOperation::RotateX(fa.animate(&ta, procedure)?))
- },
- (&TransformOperation::RotateY(fa), &TransformOperation::RotateY(ta)) => {
- Ok(TransformOperation::RotateY(fa.animate(&ta, procedure)?))
- },
- (&TransformOperation::RotateZ(fa), &TransformOperation::RotateZ(ta)) => {
- Ok(TransformOperation::RotateZ(fa.animate(&ta, procedure)?))
- },
- (&TransformOperation::Rotate(fa), &TransformOperation::Rotate(ta)) => {
- Ok(TransformOperation::Rotate(fa.animate(&ta, procedure)?))
- },
- (&TransformOperation::Rotate(fa), &TransformOperation::RotateZ(ta)) => {
- Ok(TransformOperation::Rotate(fa.animate(&ta, procedure)?))
- },
- (&TransformOperation::RotateZ(fa), &TransformOperation::Rotate(ta)) => {
- Ok(TransformOperation::Rotate(fa.animate(&ta, procedure)?))
- },
- (
- &TransformOperation::Perspective(ref fd),
- &TransformOperation::Perspective(ref td),
- ) => {
- use crate::values::computed::CSSPixelLength;
- use crate::values::generics::transform::create_perspective_matrix;
-
- // From https://drafts.csswg.org/css-transforms-2/#interpolation-of-transform-functions:
- //
- // The transform functions matrix(), matrix3d() and
- // perspective() get converted into 4x4 matrices first and
- // interpolated as defined in section Interpolation of
- // Matrices afterwards.
- //
- let from = create_perspective_matrix(fd.infinity_or(|l| l.px()));
- let to = create_perspective_matrix(td.infinity_or(|l| l.px()));
-
- let interpolated = Matrix3D::from(from).animate(&Matrix3D::from(to), procedure)?;
-
- let decomposed = decompose_3d_matrix(interpolated)?;
- let perspective_z = decomposed.perspective.2;
- // Clamp results outside of the -1 to 0 range so that we get perspective
- // function values between 1 and infinity.
- let used_value = if perspective_z >= 0. {
- transform::PerspectiveFunction::None
- } else {
- transform::PerspectiveFunction::Length(CSSPixelLength::new(
- if perspective_z <= -1. {
- 1.
- } else {
- -1. / perspective_z
- },
- ))
- };
- Ok(TransformOperation::Perspective(used_value))
- },
- _ if self.is_translate() && other.is_translate() => self
- .to_translate_3d()
- .animate(&other.to_translate_3d(), procedure),
- _ if self.is_scale() && other.is_scale() => {
- self.to_scale_3d().animate(&other.to_scale_3d(), procedure)
- },
- _ if self.is_rotate() && other.is_rotate() => self
- .to_rotate_3d()
- .animate(&other.to_rotate_3d(), procedure),
- _ => Err(()),
- }
- }
-}
-
-impl ComputedTransformOperation {
- /// If there are no size dependencies, we try to animate in-place, to avoid
- /// creating deeply nested Interpolate* operations.
- fn try_animate_mismatched_transforms_in_place(
- left: &[Self],
- right: &[Self],
- procedure: Procedure,
- ) -> Result<Self, ()> {
- let (left, _left_3d) = Transform::components_to_transform_3d_matrix(left, None)?;
- let (right, _right_3d) = Transform::components_to_transform_3d_matrix(right, None)?;
- Ok(Self::Matrix3D(
- Matrix3D::from(left).animate(&Matrix3D::from(right), procedure)?,
- ))
- }
-
- fn animate_mismatched_transforms(
- left: &[Self],
- right: &[Self],
- procedure: Procedure,
- ) -> Result<Self, ()> {
- if let Ok(op) = Self::try_animate_mismatched_transforms_in_place(left, right, procedure) {
- return Ok(op);
- }
- let from_list = Transform(left.to_vec().into());
- let to_list = Transform(right.to_vec().into());
- Ok(match procedure {
- Procedure::Add => {
- debug_assert!(false, "Addition should've been handled earlier");
- return Err(());
- },
- Procedure::Interpolate { progress } => Self::InterpolateMatrix {
- from_list,
- to_list,
- progress: Percentage(progress as f32),
- },
- Procedure::Accumulate { count } => Self::AccumulateMatrix {
- from_list,
- to_list,
- count: cmp::min(count, i32::max_value() as u64) as i32,
- },
- })
- }
-}
-
-// This might not be the most useful definition of distance. It might be better, for example,
-// to trace the distance travelled by a point as its transform is interpolated between the two
-// lists. That, however, proves to be quite complicated so we take a simple approach for now.
-// See https://bugzilla.mozilla.org/show_bug.cgi?id=1318591#c0.
-impl ComputeSquaredDistance for ComputedTransformOperation {
- fn compute_squared_distance(&self, other: &Self) -> Result<SquaredDistance, ()> {
- match (self, other) {
- (&TransformOperation::Matrix3D(ref this), &TransformOperation::Matrix3D(ref other)) => {
- this.compute_squared_distance(other)
- },
- (&TransformOperation::Matrix(ref this), &TransformOperation::Matrix(ref other)) => {
- let this: Matrix3D = (*this).into();
- let other: Matrix3D = (*other).into();
- this.compute_squared_distance(&other)
- },
- (
- &TransformOperation::Skew(ref fx, ref fy),
- &TransformOperation::Skew(ref tx, ref ty),
- ) => Ok(fx.compute_squared_distance(&tx)? + fy.compute_squared_distance(&ty)?),
- (&TransformOperation::SkewX(ref f), &TransformOperation::SkewX(ref t)) |
- (&TransformOperation::SkewY(ref f), &TransformOperation::SkewY(ref t)) => {
- f.compute_squared_distance(&t)
- },
- (
- &TransformOperation::Translate3D(ref fx, ref fy, ref fz),
- &TransformOperation::Translate3D(ref tx, ref ty, ref tz),
- ) => {
- // For translate, We don't want to require doing layout in order
- // to calculate the result, so drop the percentage part.
- //
- // However, dropping percentage makes us impossible to compute
- // the distance for the percentage-percentage case, but Gecko
- // uses the same formula, so it's fine for now.
- let basis = Length::new(0.);
- let fx = fx.resolve(basis).px();
- let fy = fy.resolve(basis).px();
- let tx = tx.resolve(basis).px();
- let ty = ty.resolve(basis).px();
-
- Ok(fx.compute_squared_distance(&tx)? +
- fy.compute_squared_distance(&ty)? +
- fz.compute_squared_distance(&tz)?)
- },
- (
- &TransformOperation::Scale3D(ref fx, ref fy, ref fz),
- &TransformOperation::Scale3D(ref tx, ref ty, ref tz),
- ) => Ok(fx.compute_squared_distance(&tx)? +
- fy.compute_squared_distance(&ty)? +
- fz.compute_squared_distance(&tz)?),
- (
- &TransformOperation::Rotate3D(fx, fy, fz, fa),
- &TransformOperation::Rotate3D(tx, ty, tz, ta),
- ) => Rotate::Rotate3D(fx, fy, fz, fa)
- .compute_squared_distance(&Rotate::Rotate3D(tx, ty, tz, ta)),
- (&TransformOperation::RotateX(fa), &TransformOperation::RotateX(ta)) |
- (&TransformOperation::RotateY(fa), &TransformOperation::RotateY(ta)) |
- (&TransformOperation::RotateZ(fa), &TransformOperation::RotateZ(ta)) |
- (&TransformOperation::Rotate(fa), &TransformOperation::Rotate(ta)) => {
- fa.compute_squared_distance(&ta)
- },
- (
- &TransformOperation::Perspective(ref fd),
- &TransformOperation::Perspective(ref td),
- ) => fd
- .infinity_or(|l| l.px())
- .compute_squared_distance(&td.infinity_or(|l| l.px())),
- (&TransformOperation::Perspective(ref p), &TransformOperation::Matrix3D(ref m)) |
- (&TransformOperation::Matrix3D(ref m), &TransformOperation::Perspective(ref p)) => {
- // FIXME(emilio): Is this right? Why interpolating this with
- // Perspective but not with anything else?
- let mut p_matrix = Matrix3D::identity();
- let p = p.infinity_or(|p| p.px());
- if p >= 0. {
- p_matrix.m34 = -1. / p.max(1.);
- }
- p_matrix.compute_squared_distance(&m)
- },
- // Gecko cross-interpolates amongst all translate and all scale
- // functions (See ToPrimitive in layout/style/StyleAnimationValue.cpp)
- // without falling back to InterpolateMatrix
- _ if self.is_translate() && other.is_translate() => self
- .to_translate_3d()
- .compute_squared_distance(&other.to_translate_3d()),
- _ if self.is_scale() && other.is_scale() => self
- .to_scale_3d()
- .compute_squared_distance(&other.to_scale_3d()),
- _ if self.is_rotate() && other.is_rotate() => self
- .to_rotate_3d()
- .compute_squared_distance(&other.to_rotate_3d()),
- _ => Err(()),
- }
- }
-}
-
-// ------------------------------------
-// Individual transforms.
-// ------------------------------------
-/// <https://drafts.csswg.org/css-transforms-2/#propdef-rotate>
-impl ComputedRotate {
- fn resolve(&self) -> (Number, Number, Number, Angle) {
- // According to the spec:
- // https://drafts.csswg.org/css-transforms-2/#individual-transforms
- //
- // If the axis is unspecified, it defaults to "0 0 1"
- match *self {
- Rotate::None => (0., 0., 1., Angle::zero()),
- Rotate::Rotate3D(rx, ry, rz, angle) => (rx, ry, rz, angle),
- Rotate::Rotate(angle) => (0., 0., 1., angle),
- }
- }
-}
-
-impl Animate for ComputedRotate {
- #[inline]
- fn animate(&self, other: &Self, procedure: Procedure) -> Result<Self, ()> {
- match (self, other) {
- (&Rotate::None, &Rotate::None) => Ok(Rotate::None),
- (&Rotate::Rotate3D(fx, fy, fz, fa), &Rotate::None) => {
- // We always normalize direction vector for rotate3d() first, so we should also
- // apply the same rule for rotate property. In other words, we promote none into
- // a 3d rotate, and normalize both direction vector first, and then do
- // interpolation.
- let (fx, fy, fz, fa) = transform::get_normalized_vector_and_angle(fx, fy, fz, fa);
- Ok(Rotate::Rotate3D(
- fx,
- fy,
- fz,
- fa.animate(&Angle::zero(), procedure)?,
- ))
- },
- (&Rotate::None, &Rotate::Rotate3D(tx, ty, tz, ta)) => {
- // Normalize direction vector first.
- let (tx, ty, tz, ta) = transform::get_normalized_vector_and_angle(tx, ty, tz, ta);
- Ok(Rotate::Rotate3D(
- tx,
- ty,
- tz,
- Angle::zero().animate(&ta, procedure)?,
- ))
- },
- (&Rotate::Rotate3D(_, ..), _) | (_, &Rotate::Rotate3D(_, ..)) => {
- let (from, to) = (self.resolve(), other.resolve());
- let (mut fx, mut fy, mut fz, fa) =
- transform::get_normalized_vector_and_angle(from.0, from.1, from.2, from.3);
- let (mut tx, mut ty, mut tz, ta) =
- transform::get_normalized_vector_and_angle(to.0, to.1, to.2, to.3);
-
- if fa == Angle::from_degrees(0.) {
- fx = tx;
- fy = ty;
- fz = tz;
- } else if ta == Angle::from_degrees(0.) {
- tx = fx;
- ty = fy;
- tz = fz;
- }
-
- if (fx, fy, fz) == (tx, ty, tz) {
- return Ok(Rotate::Rotate3D(fx, fy, fz, fa.animate(&ta, procedure)?));
- }
-
- let fv = DirectionVector::new(fx, fy, fz);
- let tv = DirectionVector::new(tx, ty, tz);
- let fq = Quaternion::from_direction_and_angle(&fv, fa.radians64());
- let tq = Quaternion::from_direction_and_angle(&tv, ta.radians64());
-
- let rq = Quaternion::animate(&fq, &tq, procedure)?;
- let (x, y, z, angle) = transform::get_normalized_vector_and_angle(
- rq.0 as f32,
- rq.1 as f32,
- rq.2 as f32,
- rq.3.acos() as f32 * 2.0,
- );
-
- Ok(Rotate::Rotate3D(x, y, z, Angle::from_radians(angle)))
- },
- (&Rotate::Rotate(_), _) | (_, &Rotate::Rotate(_)) => {
- // If this is a 2D rotation, we just animate the <angle>
- let (from, to) = (self.resolve().3, other.resolve().3);
- Ok(Rotate::Rotate(from.animate(&to, procedure)?))
- },
- }
- }
-}
-
-impl ComputeSquaredDistance for ComputedRotate {
- #[inline]
- fn compute_squared_distance(&self, other: &Self) -> Result<SquaredDistance, ()> {
- match (self, other) {
- (&Rotate::None, &Rotate::None) => Ok(SquaredDistance::from_sqrt(0.)),
- (&Rotate::Rotate3D(_, _, _, a), &Rotate::None) |
- (&Rotate::None, &Rotate::Rotate3D(_, _, _, a)) => {
- a.compute_squared_distance(&Angle::zero())
- },
- (&Rotate::Rotate3D(_, ..), _) | (_, &Rotate::Rotate3D(_, ..)) => {
- let (from, to) = (self.resolve(), other.resolve());
- let (mut fx, mut fy, mut fz, angle1) =
- transform::get_normalized_vector_and_angle(from.0, from.1, from.2, from.3);
- let (mut tx, mut ty, mut tz, angle2) =
- transform::get_normalized_vector_and_angle(to.0, to.1, to.2, to.3);
-
- if angle1 == Angle::zero() {
- fx = tx;
- fy = ty;
- fz = tz;
- } else if angle2 == Angle::zero() {
- tx = fx;
- ty = fy;
- tz = fz;
- }
-
- if (fx, fy, fz) == (tx, ty, tz) {
- angle1.compute_squared_distance(&angle2)
- } else {
- let v1 = DirectionVector::new(fx, fy, fz);
- let v2 = DirectionVector::new(tx, ty, tz);
- let q1 = Quaternion::from_direction_and_angle(&v1, angle1.radians64());
- let q2 = Quaternion::from_direction_and_angle(&v2, angle2.radians64());
- q1.compute_squared_distance(&q2)
- }
- },
- (&Rotate::Rotate(_), _) | (_, &Rotate::Rotate(_)) => self
- .resolve()
- .3
- .compute_squared_distance(&other.resolve().3),
- }
- }
-}
-
-/// <https://drafts.csswg.org/css-transforms-2/#propdef-translate>
-impl ComputedTranslate {
- fn resolve(&self) -> (LengthPercentage, LengthPercentage, Length) {
- // According to the spec:
- // https://drafts.csswg.org/css-transforms-2/#individual-transforms
- //
- // Unspecified translations default to 0px
- match *self {
- Translate::None => (
- LengthPercentage::zero(),
- LengthPercentage::zero(),
- Length::zero(),
- ),
- Translate::Translate(ref tx, ref ty, ref tz) => (tx.clone(), ty.clone(), tz.clone()),
- }
- }
-}
-
-impl Animate for ComputedTranslate {
- #[inline]
- fn animate(&self, other: &Self, procedure: Procedure) -> Result<Self, ()> {
- match (self, other) {
- (&Translate::None, &Translate::None) => Ok(Translate::None),
- (&Translate::Translate(_, ..), _) | (_, &Translate::Translate(_, ..)) => {
- let (from, to) = (self.resolve(), other.resolve());
- Ok(Translate::Translate(
- from.0.animate(&to.0, procedure)?,
- from.1.animate(&to.1, procedure)?,
- from.2.animate(&to.2, procedure)?,
- ))
- },
- }
- }
-}
-
-impl ComputeSquaredDistance for ComputedTranslate {
- #[inline]
- fn compute_squared_distance(&self, other: &Self) -> Result<SquaredDistance, ()> {
- let (from, to) = (self.resolve(), other.resolve());
- Ok(from.0.compute_squared_distance(&to.0)? +
- from.1.compute_squared_distance(&to.1)? +
- from.2.compute_squared_distance(&to.2)?)
- }
-}
-
-/// <https://drafts.csswg.org/css-transforms-2/#propdef-scale>
-impl ComputedScale {
- fn resolve(&self) -> (Number, Number, Number) {
- // According to the spec:
- // https://drafts.csswg.org/css-transforms-2/#individual-transforms
- //
- // Unspecified scales default to 1
- match *self {
- Scale::None => (1.0, 1.0, 1.0),
- Scale::Scale(sx, sy, sz) => (sx, sy, sz),
- }
- }
-}
-
-impl Animate for ComputedScale {
- #[inline]
- fn animate(&self, other: &Self, procedure: Procedure) -> Result<Self, ()> {
- match (self, other) {
- (&Scale::None, &Scale::None) => Ok(Scale::None),
- (&Scale::Scale(_, ..), _) | (_, &Scale::Scale(_, ..)) => {
- let (from, to) = (self.resolve(), other.resolve());
- // For transform lists, we add by appending to the list of
- // transform functions. However, ComputedScale cannot be
- // simply concatenated, so we have to calculate the additive
- // result here.
- if procedure == Procedure::Add {
- // scale(x1,y1,z1)*scale(x2,y2,z2) = scale(x1*x2, y1*y2, z1*z2)
- return Ok(Scale::Scale(from.0 * to.0, from.1 * to.1, from.2 * to.2));
- }
- Ok(Scale::Scale(
- animate_multiplicative_factor(from.0, to.0, procedure)?,
- animate_multiplicative_factor(from.1, to.1, procedure)?,
- animate_multiplicative_factor(from.2, to.2, procedure)?,
- ))
- },
- }
- }
-}
-
-impl ComputeSquaredDistance for ComputedScale {
- #[inline]
- fn compute_squared_distance(&self, other: &Self) -> Result<SquaredDistance, ()> {
- let (from, to) = (self.resolve(), other.resolve());
- Ok(from.0.compute_squared_distance(&to.0)? +
- from.1.compute_squared_distance(&to.1)? +
- from.2.compute_squared_distance(&to.2)?)
- }
-}
diff --git a/components/style/values/computed/align.rs b/components/style/values/computed/align.rs
deleted file mode 100644
index 94847fd80f0..00000000000
--- a/components/style/values/computed/align.rs
+++ /dev/null
@@ -1,91 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
-
-//! Values for CSS Box Alignment properties
-//!
-//! https://drafts.csswg.org/css-align/
-
-use crate::values::computed::{Context, ToComputedValue};
-use crate::values::specified;
-
-pub use super::specified::{
- AlignContent, AlignItems, AlignTracks, ContentDistribution, JustifyContent, JustifyTracks,
- SelfAlignment,
-};
-pub use super::specified::{AlignSelf, JustifySelf};
-
-/// The computed value for the `justify-items` property.
-///
-/// Need to carry around both the specified and computed value to handle the
-/// special legacy keyword without destroying style sharing.
-///
-/// In particular, `justify-items` is a reset property, so we ought to be able
-/// to share its computed representation across elements as long as they match
-/// the same rules. Except that it's not true if the specified value for
-/// `justify-items` is `legacy` and the computed value of the parent has the
-/// `legacy` modifier.
-///
-/// So instead of computing `legacy` "normally" looking at get_parent_position(),
-/// marking it as uncacheable, we carry the specified value around and handle
-/// the special case in `StyleAdjuster` instead, only when the result of the
-/// computation would vary.
-///
-/// Note that we also need to special-case this property in matching.rs, in
-/// order to properly handle changes to the legacy keyword... This all kinda
-/// sucks :(.
-///
-/// See the discussion in https://bugzil.la/1384542.
-#[derive(Clone, Copy, Debug, Eq, MallocSizeOf, PartialEq, ToCss, ToResolvedValue)]
-#[repr(C)]
-pub struct ComputedJustifyItems {
- /// The specified value for the property. Can contain the bare `legacy`
- /// keyword.
- #[css(skip)]
- pub specified: specified::JustifyItems,
- /// The computed value for the property. Cannot contain the bare `legacy`
- /// keyword, but note that it could contain it in combination with other
- /// keywords like `left`, `right` or `center`.
- pub computed: specified::JustifyItems,
-}
-
-pub use self::ComputedJustifyItems as JustifyItems;
-
-impl JustifyItems {
- /// Returns the `legacy` value.
- pub fn legacy() -> Self {
- Self {
- specified: specified::JustifyItems::legacy(),
- computed: specified::JustifyItems::normal(),
- }
- }
-}
-
-impl ToComputedValue for specified::JustifyItems {
- type ComputedValue = JustifyItems;
-
- /// <https://drafts.csswg.org/css-align/#valdef-justify-items-legacy>
- fn to_computed_value(&self, _context: &Context) -> JustifyItems {
- use crate::values::specified::align;
- let specified = *self;
- let computed = if self.0 != align::AlignFlags::LEGACY {
- *self
- } else {
- // If the inherited value of `justify-items` includes the
- // `legacy` keyword, `legacy` computes to the inherited value, but
- // we assume it computes to `normal`, and handle that special-case
- // in StyleAdjuster.
- Self::normal()
- };
-
- JustifyItems {
- specified,
- computed,
- }
- }
-
- #[inline]
- fn from_computed_value(computed: &JustifyItems) -> Self {
- computed.specified
- }
-}
diff --git a/components/style/values/computed/angle.rs b/components/style/values/computed/angle.rs
deleted file mode 100644
index ea321d22337..00000000000
--- a/components/style/values/computed/angle.rs
+++ /dev/null
@@ -1,101 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
-
-//! Computed angles.
-
-use crate::values::distance::{ComputeSquaredDistance, SquaredDistance};
-use crate::values::CSSFloat;
-use crate::Zero;
-use std::f64::consts::PI;
-use std::fmt::{self, Write};
-use std::{f32, f64};
-use style_traits::{CssWriter, ToCss};
-
-/// A computed angle in degrees.
-#[derive(
- Add,
- Animate,
- Clone,
- Copy,
- Debug,
- Deserialize,
- MallocSizeOf,
- PartialEq,
- PartialOrd,
- Serialize,
- ToAnimatedZero,
- ToResolvedValue,
-)]
-#[repr(C)]
-pub struct Angle(CSSFloat);
-
-impl ToCss for Angle {
- fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
- where
- W: Write,
- {
- self.degrees().to_css(dest)?;
- dest.write_str("deg")
- }
-}
-
-const RAD_PER_DEG: f64 = PI / 180.0;
-
-impl Angle {
- /// Creates a computed `Angle` value from a radian amount.
- pub fn from_radians(radians: CSSFloat) -> Self {
- Angle(radians / RAD_PER_DEG as f32)
- }
-
- /// Creates a computed `Angle` value from a degrees amount.
- #[inline]
- pub fn from_degrees(degrees: CSSFloat) -> Self {
- Angle(degrees)
- }
-
- /// Returns the amount of radians this angle represents.
- #[inline]
- pub fn radians(&self) -> CSSFloat {
- self.radians64().min(f32::MAX as f64).max(f32::MIN as f64) as f32
- }
-
- /// Returns the amount of radians this angle represents as a `f64`.
- ///
- /// Gecko stores angles as singles, but does this computation using doubles.
- ///
- /// This is significant enough to mess up rounding to the nearest
- /// quarter-turn for 225 degrees, for example.
- #[inline]
- pub fn radians64(&self) -> f64 {
- self.0 as f64 * RAD_PER_DEG
- }
-
- /// Return the value in degrees.
- #[inline]
- pub fn degrees(&self) -> CSSFloat {
- self.0
- }
-}
-
-impl Zero for Angle {
- #[inline]
- fn zero() -> Self {
- Angle(0.0)
- }
-
- #[inline]
- fn is_zero(&self) -> bool {
- self.0 == 0.
- }
-}
-
-impl ComputeSquaredDistance for Angle {
- #[inline]
- fn compute_squared_distance(&self, other: &Self) -> Result<SquaredDistance, ()> {
- // Use the formula for calculating the distance between angles defined in SVG:
- // https://www.w3.org/TR/SVG/animate.html#complexDistances
- self.radians64()
- .compute_squared_distance(&other.radians64())
- }
-}
diff --git a/components/style/values/computed/animation.rs b/components/style/values/computed/animation.rs
deleted file mode 100644
index 81e702a3b65..00000000000
--- a/components/style/values/computed/animation.rs
+++ /dev/null
@@ -1,69 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
-
-//! Computed values for properties related to animations and transitions
-
-use crate::values::computed::{Context, LengthPercentage, ToComputedValue};
-use crate::values::generics::animation as generics;
-use crate::values::specified::animation as specified;
-use std::fmt::{self, Write};
-use style_traits::{CssWriter, ToCss};
-
-pub use crate::values::specified::animation::{
- AnimationName, ScrollAxis, ScrollTimelineName, TransitionProperty,
-};
-
-/// A computed value for the `animation-iteration-count` property.
-#[derive(Clone, Copy, Debug, MallocSizeOf, PartialEq, ToResolvedValue, ToShmem)]
-#[repr(C)]
-pub struct AnimationIterationCount(pub f32);
-
-impl ToComputedValue for specified::AnimationIterationCount {
- type ComputedValue = AnimationIterationCount;
-
- #[inline]
- fn to_computed_value(&self, context: &Context) -> Self::ComputedValue {
- AnimationIterationCount(match *self {
- specified::AnimationIterationCount::Number(n) => n.to_computed_value(context).0,
- specified::AnimationIterationCount::Infinite => f32::INFINITY,
- })
- }
-
- #[inline]
- fn from_computed_value(computed: &Self::ComputedValue) -> Self {
- use crate::values::specified::NonNegativeNumber;
- if computed.0.is_infinite() {
- specified::AnimationIterationCount::Infinite
- } else {
- specified::AnimationIterationCount::Number(NonNegativeNumber::new(computed.0))
- }
- }
-}
-
-impl AnimationIterationCount {
- /// Returns the value `1.0`.
- #[inline]
- pub fn one() -> Self {
- Self(1.0)
- }
-}
-
-impl ToCss for AnimationIterationCount {
- fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
- where
- W: Write,
- {
- if self.0.is_infinite() {
- dest.write_str("infinite")
- } else {
- self.0.to_css(dest)
- }
- }
-}
-
-/// A computed value for the `animation-timeline` property.
-pub type AnimationTimeline = generics::GenericAnimationTimeline<LengthPercentage>;
-
-/// A computed value for the `view-timeline-inset` property.
-pub type ViewTimelineInset = generics::GenericViewTimelineInset<LengthPercentage>;
diff --git a/components/style/values/computed/background.rs b/components/style/values/computed/background.rs
deleted file mode 100644
index e2a58f8b74e..00000000000
--- a/components/style/values/computed/background.rs
+++ /dev/null
@@ -1,13 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
-
-//! Computed types for CSS values related to backgrounds.
-
-use crate::values::computed::length::NonNegativeLengthPercentage;
-use crate::values::generics::background::BackgroundSize as GenericBackgroundSize;
-
-pub use crate::values::specified::background::BackgroundRepeat;
-
-/// A computed value for the `background-size` property.
-pub type BackgroundSize = GenericBackgroundSize<NonNegativeLengthPercentage>;
diff --git a/components/style/values/computed/basic_shape.rs b/components/style/values/computed/basic_shape.rs
deleted file mode 100644
index fa30220157b..00000000000
--- a/components/style/values/computed/basic_shape.rs
+++ /dev/null
@@ -1,42 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
-
-//! CSS handling for the computed value of
-//! [`basic-shape`][basic-shape]s
-//!
-//! [basic-shape]: https://drafts.csswg.org/css-shapes/#typedef-basic-shape
-
-use crate::values::computed::url::ComputedUrl;
-use crate::values::computed::{Image, LengthPercentage, NonNegativeLengthPercentage};
-use crate::values::generics::basic_shape as generic;
-
-/// A computed alias for FillRule.
-pub use crate::values::generics::basic_shape::FillRule;
-
-/// A computed `clip-path` value.
-pub type ClipPath = generic::GenericClipPath<BasicShape, ComputedUrl>;
-
-/// A computed `shape-outside` value.
-pub type ShapeOutside = generic::GenericShapeOutside<BasicShape, Image>;
-
-/// A computed basic shape.
-pub type BasicShape = generic::GenericBasicShape<
- LengthPercentage,
- LengthPercentage,
- LengthPercentage,
- NonNegativeLengthPercentage,
->;
-
-/// The computed value of `inset()`
-pub type InsetRect = generic::InsetRect<LengthPercentage, NonNegativeLengthPercentage>;
-
-/// A computed circle.
-pub type Circle = generic::Circle<LengthPercentage, LengthPercentage, NonNegativeLengthPercentage>;
-
-/// A computed ellipse.
-pub type Ellipse =
- generic::Ellipse<LengthPercentage, LengthPercentage, NonNegativeLengthPercentage>;
-
-/// The computed value of `ShapeRadius`
-pub type ShapeRadius = generic::GenericShapeRadius<NonNegativeLengthPercentage>;
diff --git a/components/style/values/computed/border.rs b/components/style/values/computed/border.rs
deleted file mode 100644
index e073f671b39..00000000000
--- a/components/style/values/computed/border.rs
+++ /dev/null
@@ -1,84 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
-
-//! Computed types for CSS values related to borders.
-
-use crate::values::computed::length::{NonNegativeLength, NonNegativeLengthPercentage};
-use crate::values::computed::{NonNegativeNumber, NonNegativeNumberOrPercentage};
-use crate::values::generics::border::BorderCornerRadius as GenericBorderCornerRadius;
-use crate::values::generics::border::BorderImageSlice as GenericBorderImageSlice;
-use crate::values::generics::border::BorderRadius as GenericBorderRadius;
-use crate::values::generics::border::BorderSpacing as GenericBorderSpacing;
-use crate::values::generics::border::GenericBorderImageSideWidth;
-use crate::values::generics::rect::Rect;
-use crate::values::generics::size::Size2D;
-use crate::values::generics::NonNegative;
-use crate::Zero;
-use app_units::Au;
-
-pub use crate::values::specified::border::BorderImageRepeat;
-
-/// A computed value for -webkit-text-stroke-width.
-pub type LineWidth = Au;
-
-/// A computed value for border-width (and the like).
-pub type BorderSideWidth = Au;
-
-/// A computed value for the `border-image-width` property.
-pub type BorderImageWidth = Rect<BorderImageSideWidth>;
-
-/// A computed value for a single side of a `border-image-width` property.
-pub type BorderImageSideWidth =
- GenericBorderImageSideWidth<NonNegativeLengthPercentage, NonNegativeNumber>;
-
-/// A computed value for the `border-image-slice` property.
-pub type BorderImageSlice = GenericBorderImageSlice<NonNegativeNumberOrPercentage>;
-
-/// A computed value for the `border-radius` property.
-pub type BorderRadius = GenericBorderRadius<NonNegativeLengthPercentage>;
-
-/// A computed value for the `border-*-radius` longhand properties.
-pub type BorderCornerRadius = GenericBorderCornerRadius<NonNegativeLengthPercentage>;
-
-/// A computed value for the `border-spacing` longhand property.
-pub type BorderSpacing = GenericBorderSpacing<NonNegativeLength>;
-
-impl BorderImageSideWidth {
- /// Returns `1`.
- #[inline]
- pub fn one() -> Self {
- GenericBorderImageSideWidth::Number(NonNegative(1.))
- }
-}
-
-impl BorderImageSlice {
- /// Returns the `100%` value.
- #[inline]
- pub fn hundred_percent() -> Self {
- GenericBorderImageSlice {
- offsets: Rect::all(NonNegativeNumberOrPercentage::hundred_percent()),
- fill: false,
- }
- }
-}
-
-impl BorderSpacing {
- /// Returns `0 0`.
- pub fn zero() -> Self {
- GenericBorderSpacing(Size2D::new(
- NonNegativeLength::zero(),
- NonNegativeLength::zero(),
- ))
- }
-
- /// Returns the horizontal spacing.
- pub fn horizontal(&self) -> Au {
- Au::from(*self.0.width())
- }
-
- /// Returns the vertical spacing.
- pub fn vertical(&self) -> Au {
- Au::from(*self.0.height())
- }
-}
diff --git a/components/style/values/computed/box.rs b/components/style/values/computed/box.rs
deleted file mode 100644
index 5f1f68249da..00000000000
--- a/components/style/values/computed/box.rs
+++ /dev/null
@@ -1,268 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
-
-//! Computed types for box properties.
-
-use crate::values::animated::{Animate, Procedure};
-use crate::values::computed::length::{LengthPercentage, NonNegativeLength};
-use crate::values::computed::{Context, Integer, ToComputedValue};
-use crate::values::generics::box_::{
- GenericContainIntrinsicSize, GenericLineClamp, GenericPerspective, GenericVerticalAlign,
-};
-use crate::values::specified::box_ as specified;
-
-pub use crate::values::specified::box_::{
- Appearance, BaselineSource, BreakBetween, BreakWithin, Clear as SpecifiedClear, Contain,
- ContainerName, ContainerType, ContentVisibility, Display, Float as SpecifiedFloat, Overflow,
- OverflowAnchor, OverflowClipBox, OverscrollBehavior, ScrollSnapAlign, ScrollSnapAxis,
- ScrollSnapStop, ScrollSnapStrictness, ScrollSnapType, ScrollbarGutter, TouchAction, WillChange,
-};
-
-/// A computed value for the `vertical-align` property.
-pub type VerticalAlign = GenericVerticalAlign<LengthPercentage>;
-
-/// A computed value for the `contain-intrinsic-size` property.
-pub type ContainIntrinsicSize = GenericContainIntrinsicSize<NonNegativeLength>;
-
-impl ContainIntrinsicSize {
- /// Converts contain-intrinsic-size to auto style.
- pub fn add_auto_if_needed(&self) -> Option<Self> {
- use crate::Zero;
- // TODO: support contain-intrinsic-size: auto none, see
- // https://bugzilla.mozilla.org/show_bug.cgi?id=1835813
- Some(match *self {
- Self::None => Self::AutoLength(Zero::zero()),
- Self::Length(ref l) => Self::AutoLength(*l),
- Self::AutoLength(..) => return None,
- })
- }
-}
-
-/// A computed value for the `line-clamp` property.
-pub type LineClamp = GenericLineClamp<Integer>;
-
-impl Animate for LineClamp {
- #[inline]
- fn animate(&self, other: &Self, procedure: Procedure) -> Result<Self, ()> {
- if self.is_none() != other.is_none() {
- return Err(());
- }
- if self.is_none() {
- return Ok(Self::none());
- }
- Ok(Self(self.0.animate(&other.0, procedure)?.max(1)))
- }
-}
-
-/// A computed value for the `perspective` property.
-pub type Perspective = GenericPerspective<NonNegativeLength>;
-
-#[allow(missing_docs)]
-#[cfg_attr(feature = "servo", derive(Deserialize, Serialize))]
-#[derive(
- Clone,
- Copy,
- Debug,
- Eq,
- FromPrimitive,
- Hash,
- MallocSizeOf,
- Parse,
- PartialEq,
- SpecifiedValueInfo,
- ToCss,
- ToResolvedValue,
-)]
-#[repr(u8)]
-/// A computed value for the `float` property.
-pub enum Float {
- Left,
- Right,
- None,
-}
-
-impl Float {
- /// Returns true if `self` is not `None`.
- pub fn is_floating(self) -> bool {
- self != Self::None
- }
-}
-
-impl ToComputedValue for SpecifiedFloat {
- type ComputedValue = Float;
-
- #[inline]
- fn to_computed_value(&self, context: &Context) -> Self::ComputedValue {
- let ltr = context.style().writing_mode.is_bidi_ltr();
- // https://drafts.csswg.org/css-logical-props/#float-clear
- match *self {
- SpecifiedFloat::InlineStart => {
- context
- .rule_cache_conditions
- .borrow_mut()
- .set_writing_mode_dependency(context.builder.writing_mode);
- if ltr {
- Float::Left
- } else {
- Float::Right
- }
- },
- SpecifiedFloat::InlineEnd => {
- context
- .rule_cache_conditions
- .borrow_mut()
- .set_writing_mode_dependency(context.builder.writing_mode);
- if ltr {
- Float::Right
- } else {
- Float::Left
- }
- },
- SpecifiedFloat::Left => Float::Left,
- SpecifiedFloat::Right => Float::Right,
- SpecifiedFloat::None => Float::None,
- }
- }
-
- #[inline]
- fn from_computed_value(computed: &Self::ComputedValue) -> SpecifiedFloat {
- match *computed {
- Float::Left => SpecifiedFloat::Left,
- Float::Right => SpecifiedFloat::Right,
- Float::None => SpecifiedFloat::None,
- }
- }
-}
-
-#[allow(missing_docs)]
-#[cfg_attr(feature = "servo", derive(Deserialize, Serialize))]
-#[derive(
- Clone,
- Copy,
- Debug,
- Eq,
- FromPrimitive,
- Hash,
- MallocSizeOf,
- Parse,
- PartialEq,
- SpecifiedValueInfo,
- ToCss,
- ToResolvedValue,
-)]
-/// A computed value for the `clear` property.
-#[repr(u8)]
-pub enum Clear {
- None,
- Left,
- Right,
- Both,
-}
-
-impl ToComputedValue for SpecifiedClear {
- type ComputedValue = Clear;
-
- #[inline]
- fn to_computed_value(&self, context: &Context) -> Self::ComputedValue {
- let ltr = context.style().writing_mode.is_bidi_ltr();
- // https://drafts.csswg.org/css-logical-props/#float-clear
- match *self {
- SpecifiedClear::InlineStart => {
- context
- .rule_cache_conditions
- .borrow_mut()
- .set_writing_mode_dependency(context.builder.writing_mode);
- if ltr {
- Clear::Left
- } else {
- Clear::Right
- }
- },
- SpecifiedClear::InlineEnd => {
- context
- .rule_cache_conditions
- .borrow_mut()
- .set_writing_mode_dependency(context.builder.writing_mode);
- if ltr {
- Clear::Right
- } else {
- Clear::Left
- }
- },
- SpecifiedClear::None => Clear::None,
- SpecifiedClear::Left => Clear::Left,
- SpecifiedClear::Right => Clear::Right,
- SpecifiedClear::Both => Clear::Both,
- }
- }
-
- #[inline]
- fn from_computed_value(computed: &Self::ComputedValue) -> SpecifiedClear {
- match *computed {
- Clear::None => SpecifiedClear::None,
- Clear::Left => SpecifiedClear::Left,
- Clear::Right => SpecifiedClear::Right,
- Clear::Both => SpecifiedClear::Both,
- }
- }
-}
-
-/// A computed value for the `resize` property.
-#[allow(missing_docs)]
-#[cfg_attr(feature = "servo", derive(Deserialize, Serialize))]
-#[derive(Clone, Copy, Debug, Eq, Hash, MallocSizeOf, Parse, PartialEq, ToCss, ToResolvedValue)]
-#[repr(u8)]
-pub enum Resize {
- None,
- Both,
- Horizontal,
- Vertical,
-}
-
-impl ToComputedValue for specified::Resize {
- type ComputedValue = Resize;
-
- #[inline]
- fn to_computed_value(&self, context: &Context) -> Resize {
- let is_vertical = context.style().writing_mode.is_vertical();
- match self {
- specified::Resize::Inline => {
- context
- .rule_cache_conditions
- .borrow_mut()
- .set_writing_mode_dependency(context.builder.writing_mode);
- if is_vertical {
- Resize::Vertical
- } else {
- Resize::Horizontal
- }
- },
- specified::Resize::Block => {
- context
- .rule_cache_conditions
- .borrow_mut()
- .set_writing_mode_dependency(context.builder.writing_mode);
- if is_vertical {
- Resize::Horizontal
- } else {
- Resize::Vertical
- }
- },
- specified::Resize::None => Resize::None,
- specified::Resize::Both => Resize::Both,
- specified::Resize::Horizontal => Resize::Horizontal,
- specified::Resize::Vertical => Resize::Vertical,
- }
- }
-
- #[inline]
- fn from_computed_value(computed: &Resize) -> specified::Resize {
- match computed {
- Resize::None => specified::Resize::None,
- Resize::Both => specified::Resize::Both,
- Resize::Horizontal => specified::Resize::Horizontal,
- Resize::Vertical => specified::Resize::Vertical,
- }
- }
-}
diff --git a/components/style/values/computed/color.rs b/components/style/values/computed/color.rs
deleted file mode 100644
index 35cfdb9710d..00000000000
--- a/components/style/values/computed/color.rs
+++ /dev/null
@@ -1,104 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
-
-//! Computed color values.
-
-use crate::color::AbsoluteColor;
-use crate::values::animated::ToAnimatedZero;
-use crate::values::computed::percentage::Percentage;
-use crate::values::generics::color::{
- GenericCaretColor, GenericColor, GenericColorMix, GenericColorOrAuto,
-};
-use cssparser::Color as CSSParserColor;
-use std::fmt;
-use style_traits::{CssWriter, ToCss};
-
-pub use crate::values::specified::color::{ColorScheme, ForcedColorAdjust, PrintColorAdjust};
-
-/// The computed value of the `color` property.
-pub type ColorPropertyValue = AbsoluteColor;
-
-/// The computed value of `-moz-font-smoothing-background-color`.
-pub type MozFontSmoothingBackgroundColor = AbsoluteColor;
-
-/// A computed value for `<color>`.
-pub type Color = GenericColor<Percentage>;
-
-/// A computed color-mix().
-pub type ColorMix = GenericColorMix<Color, Percentage>;
-
-impl ToCss for Color {
- fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
- where
- W: fmt::Write,
- {
- match *self {
- Self::Absolute(ref c) => c.to_css(dest),
- Self::CurrentColor => cssparser::ToCss::to_css(&CSSParserColor::CurrentColor, dest),
- Self::ColorMix(ref m) => m.to_css(dest),
- }
- }
-}
-
-impl Color {
- /// Create a new computed [`Color`] from a given color-mix, simplifying it to an absolute color
- /// if possible.
- pub fn from_color_mix(color_mix: ColorMix) -> Self {
- if let Some(absolute) = color_mix.mix_to_absolute() {
- Self::Absolute(absolute)
- } else {
- Self::ColorMix(Box::new(color_mix))
- }
- }
-
- /// Returns a complex color value representing transparent.
- pub fn transparent() -> Color {
- Color::Absolute(AbsoluteColor::transparent())
- }
-
- /// Returns opaque black.
- pub fn black() -> Color {
- Color::Absolute(AbsoluteColor::black())
- }
-
- /// Returns opaque white.
- pub fn white() -> Color {
- Color::Absolute(AbsoluteColor::white())
- }
-
- /// Combine this complex color with the given foreground color into an
- /// absolute color.
- pub fn resolve_to_absolute(&self, current_color: &AbsoluteColor) -> AbsoluteColor {
- use crate::values::specified::percentage::ToPercentage;
-
- match *self {
- Self::Absolute(c) => c,
- Self::CurrentColor => *current_color,
- Self::ColorMix(ref mix) => {
- let left = mix.left.resolve_to_absolute(current_color);
- let right = mix.right.resolve_to_absolute(current_color);
- crate::color::mix::mix(
- mix.interpolation,
- &left,
- mix.left_percentage.to_percentage(),
- &right,
- mix.right_percentage.to_percentage(),
- mix.normalize_weights,
- )
- },
- }
- }
-}
-
-impl ToAnimatedZero for AbsoluteColor {
- fn to_animated_zero(&self) -> Result<Self, ()> {
- Ok(Self::transparent())
- }
-}
-
-/// auto | <color>
-pub type ColorOrAuto = GenericColorOrAuto<Color>;
-
-/// caret-color
-pub type CaretColor = GenericCaretColor<Color>;
diff --git a/components/style/values/computed/column.rs b/components/style/values/computed/column.rs
deleted file mode 100644
index 38437ea1104..00000000000
--- a/components/style/values/computed/column.rs
+++ /dev/null
@@ -1,11 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
-
-//! Computed types for the column properties.
-
-use crate::values::computed::PositiveInteger;
-use crate::values::generics::column::ColumnCount as GenericColumnCount;
-
-/// A computed type for `column-count` values.
-pub type ColumnCount = GenericColumnCount<PositiveInteger>;
diff --git a/components/style/values/computed/counters.rs b/components/style/values/computed/counters.rs
deleted file mode 100644
index fd5e915c4a8..00000000000
--- a/components/style/values/computed/counters.rs
+++ /dev/null
@@ -1,26 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
-
-//! Computed values for counter properties
-
-use crate::values::computed::image::Image;
-use crate::values::generics::counters as generics;
-use crate::values::generics::counters::CounterIncrement as GenericCounterIncrement;
-use crate::values::generics::counters::CounterReset as GenericCounterReset;
-use crate::values::generics::counters::CounterSet as GenericCounterSet;
-
-/// A computed value for the `counter-increment` property.
-pub type CounterIncrement = GenericCounterIncrement<i32>;
-
-/// A computed value for the `counter-reset` property.
-pub type CounterReset = GenericCounterReset<i32>;
-
-/// A computed value for the `counter-set` property.
-pub type CounterSet = GenericCounterSet<i32>;
-
-/// A computed value for the `content` property.
-pub type Content = generics::GenericContent<Image>;
-
-/// A computed content item.
-pub type ContentItem = generics::GenericContentItem<Image>;
diff --git a/components/style/values/computed/easing.rs b/components/style/values/computed/easing.rs
deleted file mode 100644
index d351b3c71d0..00000000000
--- a/components/style/values/computed/easing.rs
+++ /dev/null
@@ -1,109 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
-
-//! Computed types for CSS Easing functions.
-
-use euclid::approxeq::ApproxEq;
-
-use crate::bezier::Bezier;
-use crate::piecewise_linear::PiecewiseLinearFunction;
-use crate::values::computed::{Integer, Number};
-use crate::values::generics::easing::{self, BeforeFlag, StepPosition, TimingKeyword};
-
-/// A computed timing function.
-pub type ComputedTimingFunction = easing::TimingFunction<Integer, Number, PiecewiseLinearFunction>;
-
-/// An alias of the computed timing function.
-pub type TimingFunction = ComputedTimingFunction;
-
-impl ComputedTimingFunction {
- fn calculate_step_output(
- steps: i32,
- pos: StepPosition,
- progress: f64,
- before_flag: BeforeFlag,
- ) -> f64 {
- // User specified values can cause overflow (bug 1706157). Increments/decrements
- // should be gravefully handled.
- let mut current_step = (progress * (steps as f64)).floor() as i32;
-
- // Increment current step if it is jump-start or start.
- if pos == StepPosition::Start ||
- pos == StepPosition::JumpStart ||
- pos == StepPosition::JumpBoth
- {
- current_step = current_step.checked_add(1).unwrap_or(current_step);
- }
-
- // If the "before flag" is set and we are at a transition point,
- // drop back a step
- if before_flag == BeforeFlag::Set &&
- (progress * steps as f64).rem_euclid(1.0).approx_eq(&0.0)
- {
- current_step = current_step.checked_sub(1).unwrap_or(current_step);
- }
-
- // We should not produce a result outside [0, 1] unless we have an
- // input outside that range. This takes care of steps that would otherwise
- // occur at boundaries.
- if progress >= 0.0 && current_step < 0 {
- current_step = 0;
- }
-
- // |jumps| should always be in [1, i32::MAX].
- let jumps = if pos == StepPosition::JumpBoth {
- steps.checked_add(1).unwrap_or(steps)
- } else if pos == StepPosition::JumpNone {
- steps.checked_sub(1).unwrap_or(steps)
- } else {
- steps
- };
-
- if progress <= 1.0 && current_step > jumps {
- current_step = jumps;
- }
-
- (current_step as f64) / (jumps as f64)
- }
-
- /// The output of the timing function given the progress ratio of this animation.
- pub fn calculate_output(&self, progress: f64, before_flag: BeforeFlag, epsilon: f64) -> f64 {
- let progress = match self {
- TimingFunction::CubicBezier { x1, y1, x2, y2 } => {
- Bezier::calculate_bezier_output(progress, epsilon, *x1, *y1, *x2, *y2)
- },
- TimingFunction::Steps(steps, pos) => {
- Self::calculate_step_output(*steps, *pos, progress, before_flag)
- },
- TimingFunction::LinearFunction(function) => function.at(progress as f32).into(),
- TimingFunction::Keyword(keyword) => match keyword {
- TimingKeyword::Linear => progress,
- TimingKeyword::Ease => {
- Bezier::calculate_bezier_output(progress, epsilon, 0.25, 0.1, 0.25, 1.)
- },
- TimingKeyword::EaseIn => {
- Bezier::calculate_bezier_output(progress, epsilon, 0.42, 0., 1., 1.)
- },
- TimingKeyword::EaseOut => {
- Bezier::calculate_bezier_output(progress, epsilon, 0., 0., 0.58, 1.)
- },
- TimingKeyword::EaseInOut => {
- Bezier::calculate_bezier_output(progress, epsilon, 0.42, 0., 0.58, 1.)
- },
- },
- };
-
- // The output progress value of an easing function is a real number in the range:
- // [-inf, inf].
- // https://drafts.csswg.org/css-easing-1/#output-progress-value
- //
- // However, we expect to use the finite progress for interpolation and web-animations
- // https://drafts.csswg.org/css-values-4/#interpolation
- // https://drafts.csswg.org/web-animations-1/#dom-computedeffecttiming-progress
- //
- // So we clamp the infinite progress, per the spec issue:
- // https://github.com/w3c/csswg-drafts/issues/8344
- progress.min(f64::MAX).max(f64::MIN)
- }
-}
diff --git a/components/style/values/computed/effects.rs b/components/style/values/computed/effects.rs
deleted file mode 100644
index ce3498d1410..00000000000
--- a/components/style/values/computed/effects.rs
+++ /dev/null
@@ -1,44 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
-
-//! Computed types for CSS values related to effects.
-
-use crate::values::computed::color::Color;
-use crate::values::computed::length::{Length, NonNegativeLength};
-#[cfg(feature = "gecko")]
-use crate::values::computed::url::ComputedUrl;
-use crate::values::computed::{Angle, NonNegativeNumber, ZeroToOneNumber};
-use crate::values::generics::effects::BoxShadow as GenericBoxShadow;
-use crate::values::generics::effects::Filter as GenericFilter;
-use crate::values::generics::effects::SimpleShadow as GenericSimpleShadow;
-#[cfg(not(feature = "gecko"))]
-use crate::values::Impossible;
-
-/// A computed value for a single shadow of the `box-shadow` property.
-pub type BoxShadow = GenericBoxShadow<Color, Length, NonNegativeLength, Length>;
-
-/// A computed value for a single `filter`.
-#[cfg(feature = "gecko")]
-pub type Filter = GenericFilter<
- Angle,
- NonNegativeNumber,
- ZeroToOneNumber,
- NonNegativeLength,
- SimpleShadow,
- ComputedUrl,
->;
-
-/// A computed value for a single `filter`.
-#[cfg(feature = "servo")]
-pub type Filter = GenericFilter<
- Angle,
- NonNegativeNumber,
- ZeroToOneNumber,
- NonNegativeLength,
- SimpleShadow,
- Impossible,
->;
-
-/// A computed value for the `drop-shadow()` filter.
-pub type SimpleShadow = GenericSimpleShadow<Color, Length, NonNegativeLength>;
diff --git a/components/style/values/computed/flex.rs b/components/style/values/computed/flex.rs
deleted file mode 100644
index 95c497ecf63..00000000000
--- a/components/style/values/computed/flex.rs
+++ /dev/null
@@ -1,19 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
-
-//! Computed types for CSS values related to flexbox.
-
-use crate::values::computed::Size;
-use crate::values::generics::flex::FlexBasis as GenericFlexBasis;
-
-/// A computed value for the `flex-basis` property.
-pub type FlexBasis = GenericFlexBasis<Size>;
-
-impl FlexBasis {
- /// `auto`
- #[inline]
- pub fn auto() -> Self {
- GenericFlexBasis::Size(Size::auto())
- }
-}
diff --git a/components/style/values/computed/font.rs b/components/style/values/computed/font.rs
deleted file mode 100644
index 68a4391cf82..00000000000
--- a/components/style/values/computed/font.rs
+++ /dev/null
@@ -1,1281 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
-
-//! Computed values for font properties
-
-use crate::parser::{Parse, ParserContext};
-use crate::values::animated::ToAnimatedValue;
-use crate::values::computed::{
- Angle, Context, Integer, Length, NonNegativeLength, NonNegativeNumber, Number, Percentage,
- ToComputedValue,
-};
-use crate::values::generics::font::{
- FeatureTagValue, FontSettings, TaggedFontValue, VariationValue,
-};
-use crate::values::generics::{font as generics, NonNegative};
-use crate::values::specified::font::{
- self as specified, KeywordInfo, MAX_FONT_WEIGHT, MIN_FONT_WEIGHT,
-};
-use crate::values::specified::length::{FontBaseSize, NoCalcLength};
-use crate::Atom;
-use cssparser::{serialize_identifier, CssStringWriter, Parser};
-#[cfg(feature = "gecko")]
-use malloc_size_of::{MallocSizeOf, MallocSizeOfOps};
-use std::fmt::{self, Write};
-use style_traits::{CssWriter, ParseError, ToCss};
-
-pub use crate::values::computed::Length as MozScriptMinSize;
-pub use crate::values::specified::font::MozScriptSizeMultiplier;
-pub use crate::values::specified::font::{FontPalette, FontSynthesis};
-pub use crate::values::specified::font::{
- FontVariantAlternates, FontVariantEastAsian, FontVariantLigatures, FontVariantNumeric, XLang,
- XTextScale,
-};
-pub use crate::values::specified::Integer as SpecifiedInteger;
-pub use crate::values::specified::Number as SpecifiedNumber;
-
-/// Generic template for font property type classes that use a fixed-point
-/// internal representation with `FRACTION_BITS` for the fractional part.
-///
-/// Values are constructed from and exposed as floating-point, but stored
-/// internally as fixed point, so there will be a quantization effect on
-/// fractional values, depending on the number of fractional bits used.
-///
-/// Using (16-bit) fixed-point types rather than floats for these style
-/// attributes reduces the memory footprint of gfxFontEntry and gfxFontStyle; it
-/// will also tend to reduce the number of distinct font instances that get
-/// created, particularly when styles are animated or set to arbitrary values
-/// (e.g. by sliders in the UI), which should reduce pressure on graphics
-/// resources and improve cache hit rates.
-///
-/// cbindgen:derive-lt
-/// cbindgen:derive-lte
-/// cbindgen:derive-gt
-/// cbindgen:derive-gte
-#[repr(C)]
-#[derive(
- Clone,
- ComputeSquaredDistance,
- Copy,
- Debug,
- Eq,
- Hash,
- MallocSizeOf,
- PartialEq,
- PartialOrd,
- ToResolvedValue,
-)]
-#[cfg_attr(feature = "servo", derive(Deserialize, Serialize))]
-pub struct FixedPoint<T, const FRACTION_BITS: u16> {
- value: T,
-}
-
-impl<T, const FRACTION_BITS: u16> FixedPoint<T, FRACTION_BITS>
-where
- T: num_traits::cast::AsPrimitive<f32>,
- f32: num_traits::cast::AsPrimitive<T>,
-{
- const SCALE: u16 = 1 << FRACTION_BITS;
- const INVERSE_SCALE: f32 = 1.0 / Self::SCALE as f32;
-
- /// Returns a fixed-point bit from a floating-point context.
- fn from_float(v: f32) -> Self {
- use num_traits::cast::AsPrimitive;
- Self {
- value: (v * Self::SCALE as f32).round().as_(),
- }
- }
-
- /// Returns the floating-point representation.
- fn to_float(&self) -> f32 {
- self.value.as_() * Self::INVERSE_SCALE
- }
-}
-
-/// font-weight: range 1..1000, fractional values permitted; keywords
-/// 'normal', 'bold' aliased to 400, 700 respectively.
-///
-/// We use an unsigned 10.6 fixed-point value (range 0.0 - 1023.984375)
-pub const FONT_WEIGHT_FRACTION_BITS: u16 = 6;
-
-/// This is an alias which is useful mostly as a cbindgen / C++ inference
-/// workaround.
-pub type FontWeightFixedPoint = FixedPoint<u16, FONT_WEIGHT_FRACTION_BITS>;
-
-/// A value for the font-weight property per:
-///
-/// https://drafts.csswg.org/css-fonts-4/#propdef-font-weight
-///
-/// cbindgen:derive-lt
-/// cbindgen:derive-lte
-/// cbindgen:derive-gt
-/// cbindgen:derive-gte
-#[derive(
- Clone,
- ComputeSquaredDistance,
- Copy,
- Debug,
- Hash,
- MallocSizeOf,
- PartialEq,
- PartialOrd,
- ToResolvedValue,
-)]
-#[cfg_attr(feature = "servo", derive(Deserialize, Serialize))]
-#[repr(C)]
-pub struct FontWeight(FontWeightFixedPoint);
-impl ToAnimatedValue for FontWeight {
- type AnimatedValue = Number;
-
- #[inline]
- fn to_animated_value(self) -> Self::AnimatedValue {
- self.value()
- }
-
- #[inline]
- fn from_animated_value(animated: Self::AnimatedValue) -> Self {
- FontWeight::from_float(animated)
- }
-}
-
-impl ToCss for FontWeight {
- fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
- where
- W: fmt::Write,
- {
- self.value().to_css(dest)
- }
-}
-
-impl FontWeight {
- /// The `normal` keyword.
- pub const NORMAL: FontWeight = FontWeight(FontWeightFixedPoint {
- value: 400 << FONT_WEIGHT_FRACTION_BITS,
- });
-
- /// The `bold` value.
- pub const BOLD: FontWeight = FontWeight(FontWeightFixedPoint {
- value: 700 << FONT_WEIGHT_FRACTION_BITS,
- });
-
- /// The threshold from which we consider a font bold.
- pub const BOLD_THRESHOLD: FontWeight = FontWeight(FontWeightFixedPoint {
- value: 600 << FONT_WEIGHT_FRACTION_BITS,
- });
-
- /// Returns the `normal` keyword value.
- pub fn normal() -> Self {
- Self::NORMAL
- }
-
- /// Weither this weight is bold
- pub fn is_bold(&self) -> bool {
- *self >= Self::BOLD_THRESHOLD
- }
-
- /// Returns the value as a float.
- pub fn value(&self) -> f32 {
- self.0.to_float()
- }
-
- /// Construct a valid weight from a float value.
- pub fn from_float(v: f32) -> Self {
- Self(FixedPoint::from_float(
- v.max(MIN_FONT_WEIGHT).min(MAX_FONT_WEIGHT),
- ))
- }
-
- /// Return the bolder weight.
- ///
- /// See the table in:
- /// https://drafts.csswg.org/css-fonts-4/#font-weight-numeric-values
- pub fn bolder(self) -> Self {
- let value = self.value();
- if value < 350. {
- return Self::NORMAL;
- }
- if value < 550. {
- return Self::BOLD;
- }
- Self::from_float(value.max(900.))
- }
-
- /// Return the lighter weight.
- ///
- /// See the table in:
- /// https://drafts.csswg.org/css-fonts-4/#font-weight-numeric-values
- pub fn lighter(self) -> Self {
- let value = self.value();
- if value < 550. {
- return Self::from_float(value.min(100.));
- }
- if value < 750. {
- return Self::NORMAL;
- }
- Self::BOLD
- }
-}
-
-#[derive(
- Animate,
- Clone,
- ComputeSquaredDistance,
- Copy,
- Debug,
- MallocSizeOf,
- PartialEq,
- ToAnimatedZero,
- ToCss,
- ToResolvedValue,
-)]
-#[cfg_attr(feature = "servo", derive(Serialize, Deserialize))]
-/// The computed value of font-size
-pub struct FontSize {
- /// The computed size, that we use to compute ems etc. This accounts for
- /// e.g., text-zoom.
- pub computed_size: NonNegativeLength,
- /// The actual used size. This is the computed font size, potentially
- /// constrained by other factors like minimum font-size settings and so on.
- #[css(skip)]
- pub used_size: NonNegativeLength,
- /// If derived from a keyword, the keyword and additional transformations applied to it
- #[css(skip)]
- pub keyword_info: KeywordInfo,
-}
-
-impl FontSize {
- /// The actual computed font size.
- #[inline]
- pub fn computed_size(&self) -> Length {
- self.computed_size.0
- }
-
- /// The actual used font size.
- #[inline]
- pub fn used_size(&self) -> Length {
- self.used_size.0
- }
-
- #[inline]
- /// Get default value of font size.
- pub fn medium() -> Self {
- Self {
- computed_size: NonNegative(Length::new(specified::FONT_MEDIUM_PX)),
- used_size: NonNegative(Length::new(specified::FONT_MEDIUM_PX)),
- keyword_info: KeywordInfo::medium(),
- }
- }
-}
-
-impl ToAnimatedValue for FontSize {
- type AnimatedValue = Length;
-
- #[inline]
- fn to_animated_value(self) -> Self::AnimatedValue {
- self.computed_size.0
- }
-
- #[inline]
- fn from_animated_value(animated: Self::AnimatedValue) -> Self {
- FontSize {
- computed_size: NonNegative(animated.clamp_to_non_negative()),
- used_size: NonNegative(animated.clamp_to_non_negative()),
- keyword_info: KeywordInfo::none(),
- }
- }
-}
-
-#[derive(Clone, Debug, Eq, PartialEq, ToComputedValue, ToResolvedValue)]
-#[cfg_attr(feature = "servo", derive(Hash, MallocSizeOf, Serialize, Deserialize))]
-/// Specifies a prioritized list of font family names or generic family names.
-#[repr(C)]
-pub struct FontFamily {
- /// The actual list of family names.
- pub families: FontFamilyList,
- /// Whether this font-family came from a specified system-font.
- pub is_system_font: bool,
- /// Whether this is the initial font-family that might react to language
- /// changes.
- pub is_initial: bool,
-}
-
-macro_rules! static_font_family {
- ($ident:ident, $family:expr) => {
- lazy_static! {
- static ref $ident: FontFamily = FontFamily {
- families: FontFamilyList {
- #[cfg(feature = "gecko")]
- list: crate::ArcSlice::from_iter_leaked(std::iter::once($family)),
- #[cfg(feature = "servo")]
- list: Box::new([$family]),
- },
- is_system_font: false,
- is_initial: false,
- };
- }
- };
-}
-
-impl FontFamily {
- #[inline]
- /// Get default font family as `serif` which is a generic font-family
- pub fn serif() -> Self {
- Self::generic(GenericFontFamily::Serif).clone()
- }
-
- /// Returns the font family for `-moz-bullet-font`.
- #[cfg(feature = "gecko")]
- pub(crate) fn moz_bullet() -> &'static Self {
- static_font_family!(
- MOZ_BULLET,
- SingleFontFamily::FamilyName(FamilyName {
- name: atom!("-moz-bullet-font"),
- syntax: FontFamilyNameSyntax::Identifiers,
- })
- );
-
- &*MOZ_BULLET
- }
-
- /// Returns a font family for a single system font.
- #[cfg(feature = "gecko")]
- pub fn for_system_font(name: &str) -> Self {
- Self {
- families: FontFamilyList {
- list: crate::ArcSlice::from_iter(std::iter::once(SingleFontFamily::FamilyName(
- FamilyName {
- name: Atom::from(name),
- syntax: FontFamilyNameSyntax::Identifiers,
- },
- ))),
- },
- is_system_font: true,
- is_initial: false,
- }
- }
-
- /// Returns a generic font family.
- pub fn generic(generic: GenericFontFamily) -> &'static Self {
- macro_rules! generic_font_family {
- ($ident:ident, $family:ident) => {
- static_font_family!(
- $ident,
- SingleFontFamily::Generic(GenericFontFamily::$family)
- )
- };
- }
-
- generic_font_family!(SERIF, Serif);
- generic_font_family!(SANS_SERIF, SansSerif);
- generic_font_family!(MONOSPACE, Monospace);
- generic_font_family!(CURSIVE, Cursive);
- generic_font_family!(FANTASY, Fantasy);
- #[cfg(feature = "gecko")]
- generic_font_family!(MOZ_EMOJI, MozEmoji);
- generic_font_family!(SYSTEM_UI, SystemUi);
-
- match generic {
- GenericFontFamily::None => {
- debug_assert!(false, "Bogus caller!");
- &*SERIF
- },
- GenericFontFamily::Serif => &*SERIF,
- GenericFontFamily::SansSerif => &*SANS_SERIF,
- GenericFontFamily::Monospace => &*MONOSPACE,
- GenericFontFamily::Cursive => &*CURSIVE,
- GenericFontFamily::Fantasy => &*FANTASY,
- #[cfg(feature = "gecko")]
- GenericFontFamily::MozEmoji => &*MOZ_EMOJI,
- GenericFontFamily::SystemUi => &*SYSTEM_UI,
- }
- }
-}
-
-#[cfg(feature = "gecko")]
-impl MallocSizeOf for FontFamily {
- fn size_of(&self, ops: &mut MallocSizeOfOps) -> usize {
- use malloc_size_of::MallocUnconditionalSizeOf;
- // SharedFontList objects are generally measured from the pointer stored
- // in the specified value. So only count this if the SharedFontList is
- // unshared.
- let shared_font_list = &self.families.list;
- if shared_font_list.is_unique() {
- shared_font_list.unconditional_size_of(ops)
- } else {
- 0
- }
- }
-}
-
-impl ToCss for FontFamily {
- fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
- where
- W: fmt::Write,
- {
- let mut iter = self.families.iter();
- match iter.next() {
- Some(f) => f.to_css(dest)?,
- None => {
- #[cfg(feature = "gecko")]
- return return Ok(());
- #[cfg(feature = "servo")]
- unreachable!();
- },
- }
- for family in iter {
- dest.write_str(", ")?;
- family.to_css(dest)?;
- }
- Ok(())
- }
-}
-
-/// The name of a font family of choice.
-#[derive(
- Clone, Debug, Eq, Hash, MallocSizeOf, PartialEq, ToComputedValue, ToResolvedValue, ToShmem,
-)]
-#[cfg_attr(feature = "servo", derive(Deserialize, Serialize))]
-#[repr(C)]
-pub struct FamilyName {
- /// Name of the font family.
- pub name: Atom,
- /// Syntax of the font family.
- pub syntax: FontFamilyNameSyntax,
-}
-
-#[cfg(feature = "gecko")]
-impl FamilyName {
- fn is_known_icon_font_family(&self) -> bool {
- use crate::gecko_bindings::bindings;
- unsafe { bindings::Gecko_IsKnownIconFontFamily(self.name.as_ptr()) }
- }
-}
-
-impl ToCss for FamilyName {
- fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
- where
- W: fmt::Write,
- {
- match self.syntax {
- FontFamilyNameSyntax::Quoted => {
- dest.write_char('"')?;
- write!(CssStringWriter::new(dest), "{}", self.name)?;
- dest.write_char('"')
- },
- FontFamilyNameSyntax::Identifiers => {
- let mut first = true;
- for ident in self.name.to_string().split(' ') {
- if first {
- first = false;
- } else {
- dest.write_char(' ')?;
- }
- debug_assert!(
- !ident.is_empty(),
- "Family name with leading, \
- trailing, or consecutive white spaces should \
- have been marked quoted by the parser"
- );
- serialize_identifier(ident, dest)?;
- }
- Ok(())
- },
- }
- }
-}
-
-#[derive(
- Clone, Copy, Debug, Eq, Hash, MallocSizeOf, PartialEq, ToComputedValue, ToResolvedValue, ToShmem,
-)]
-#[cfg_attr(feature = "servo", derive(Deserialize, Serialize))]
-/// Font family names must either be given quoted as strings,
-/// or unquoted as a sequence of one or more identifiers.
-#[repr(u8)]
-pub enum FontFamilyNameSyntax {
- /// The family name was specified in a quoted form, e.g. "Font Name"
- /// or 'Font Name'.
- Quoted,
-
- /// The family name was specified in an unquoted form as a sequence of
- /// identifiers.
- Identifiers,
-}
-
-/// A set of faces that vary in weight, width or slope.
-/// cbindgen:derive-mut-casts=true
-#[derive(
- Clone, Debug, Eq, MallocSizeOf, PartialEq, ToCss, ToComputedValue, ToResolvedValue, ToShmem,
-)]
-#[cfg_attr(feature = "servo", derive(Deserialize, Serialize, Hash))]
-#[repr(u8)]
-pub enum SingleFontFamily {
- /// The name of a font family of choice.
- FamilyName(FamilyName),
- /// Generic family name.
- Generic(GenericFontFamily),
-}
-
-fn system_ui_enabled(_: &ParserContext) -> bool {
- static_prefs::pref!("layout.css.system-ui.enabled")
-}
-
-/// A generic font-family name.
-///
-/// The order here is important, if you change it make sure that
-/// `gfxPlatformFontList.h`s ranged array and `gfxFontFamilyList`'s
-/// sSingleGenerics are updated as well.
-///
-/// NOTE(emilio): Should be u8, but it's a u32 because of ABI issues between GCC
-/// and LLVM see https://bugs.llvm.org/show_bug.cgi?id=44228 / bug 1600735 /
-/// bug 1726515.
-#[derive(
- Clone,
- Copy,
- Debug,
- Eq,
- Hash,
- MallocSizeOf,
- PartialEq,
- Parse,
- ToCss,
- ToComputedValue,
- ToResolvedValue,
- ToShmem,
-)]
-#[cfg_attr(feature = "servo", derive(Deserialize, Serialize))]
-#[repr(u32)]
-#[allow(missing_docs)]
-pub enum GenericFontFamily {
- /// No generic family specified, only for internal usage.
- ///
- /// NOTE(emilio): Gecko code relies on this variant being zero.
- #[css(skip)]
- None = 0,
- Serif,
- SansSerif,
- #[parse(aliases = "-moz-fixed")]
- Monospace,
- Cursive,
- Fantasy,
- #[parse(condition = "system_ui_enabled")]
- SystemUi,
- /// An internal value for emoji font selection.
- #[css(skip)]
- #[cfg(feature = "gecko")]
- MozEmoji,
-}
-
-impl GenericFontFamily {
- /// When we disallow websites to override fonts, we ignore some generic
- /// families that the website might specify, since they're not configured by
- /// the user. See bug 789788 and bug 1730098.
- #[cfg(feature = "gecko")]
- pub(crate) fn valid_for_user_font_prioritization(self) -> bool {
- match self {
- Self::None | Self::Fantasy | Self::Cursive | Self::SystemUi | Self::MozEmoji => false,
-
- Self::Serif | Self::SansSerif | Self::Monospace => true,
- }
- }
-}
-
-impl Parse for SingleFontFamily {
- /// Parse a font-family value.
- fn parse<'i, 't>(
- context: &ParserContext,
- input: &mut Parser<'i, 't>,
- ) -> Result<Self, ParseError<'i>> {
- if let Ok(value) = input.try_parse(|i| i.expect_string_cloned()) {
- return Ok(SingleFontFamily::FamilyName(FamilyName {
- name: Atom::from(&*value),
- syntax: FontFamilyNameSyntax::Quoted,
- }));
- }
-
- if let Ok(generic) = input.try_parse(|i| GenericFontFamily::parse(context, i)) {
- return Ok(SingleFontFamily::Generic(generic));
- }
-
- let first_ident = input.expect_ident_cloned()?;
- let reserved = match_ignore_ascii_case! { &first_ident,
- // https://drafts.csswg.org/css-fonts/#propdef-font-family
- // "Font family names that happen to be the same as a keyword value
- // (`inherit`, `serif`, `sans-serif`, `monospace`, `fantasy`, and `cursive`)
- // must be quoted to prevent confusion with the keywords with the same names.
- // The keywords ‘initial’ and ‘default’ are reserved for future use
- // and must also be quoted when used as font names.
- // UAs must not consider these keywords as matching the <family-name> type."
- "inherit" | "initial" | "unset" | "revert" | "default" => true,
- _ => false,
- };
-
- let mut value = first_ident.as_ref().to_owned();
- let mut serialize_quoted = value.contains(' ');
-
- // These keywords are not allowed by themselves.
- // The only way this value can be valid with with another keyword.
- if reserved {
- let ident = input.expect_ident()?;
- serialize_quoted = serialize_quoted || ident.contains(' ');
- value.push(' ');
- value.push_str(&ident);
- }
- while let Ok(ident) = input.try_parse(|i| i.expect_ident_cloned()) {
- serialize_quoted = serialize_quoted || ident.contains(' ');
- value.push(' ');
- value.push_str(&ident);
- }
- let syntax = if serialize_quoted {
- // For font family names which contains special white spaces, e.g.
- // `font-family: \ a\ \ b\ \ c\ ;`, it is tricky to serialize them
- // as identifiers correctly. Just mark them quoted so we don't need
- // to worry about them in serialization code.
- FontFamilyNameSyntax::Quoted
- } else {
- FontFamilyNameSyntax::Identifiers
- };
- Ok(SingleFontFamily::FamilyName(FamilyName {
- name: Atom::from(value),
- syntax,
- }))
- }
-}
-
-#[cfg(feature = "servo")]
-impl SingleFontFamily {
- /// Get the corresponding font-family with Atom
- pub fn from_atom(input: Atom) -> SingleFontFamily {
- match input {
- atom!("serif") => return SingleFontFamily::Generic(GenericFontFamily::Serif),
- atom!("sans-serif") => return SingleFontFamily::Generic(GenericFontFamily::SansSerif),
- atom!("cursive") => return SingleFontFamily::Generic(GenericFontFamily::Cursive),
- atom!("fantasy") => return SingleFontFamily::Generic(GenericFontFamily::Fantasy),
- atom!("monospace") => return SingleFontFamily::Generic(GenericFontFamily::Monospace),
- atom!("system-ui") => return SingleFontFamily::Generic(GenericFontFamily::SystemUi),
- _ => {},
- }
-
- match_ignore_ascii_case! { &input,
- "serif" => return SingleFontFamily::Generic(GenericFontFamily::Serif),
- "sans-serif" => return SingleFontFamily::Generic(GenericFontFamily::SansSerif),
- "cursive" => return SingleFontFamily::Generic(GenericFontFamily::Cursive),
- "fantasy" => return SingleFontFamily::Generic(GenericFontFamily::Fantasy),
- "monospace" => return SingleFontFamily::Generic(GenericFontFamily::Monospace),
- "system-ui" => return SingleFontFamily::Generic(GenericFontFamily::SystemUi),
- _ => {}
- }
-
- // We don't know if it's quoted or not. So we set it to
- // quoted by default.
- SingleFontFamily::FamilyName(FamilyName {
- name: input,
- syntax: FontFamilyNameSyntax::Quoted,
- })
- }
-}
-
-/// A list of font families.
-#[cfg(feature = "gecko")]
-#[derive(Clone, Debug, ToComputedValue, ToResolvedValue, ToShmem, PartialEq, Eq)]
-#[repr(C)]
-pub struct FontFamilyList {
- /// The actual list of font families specified.
- pub list: crate::ArcSlice<SingleFontFamily>,
-}
-
-/// A list of font families.
-#[cfg(feature = "servo")]
-#[derive(
- Clone,
- Debug,
- Deserialize,
- Eq,
- Hash,
- MallocSizeOf,
- PartialEq,
- Serialize,
- ToComputedValue,
- ToResolvedValue,
- ToShmem,
-)]
-pub struct FontFamilyList {
- /// The actual list of font families specified.
- pub list: Box<[SingleFontFamily]>,
-}
-
-impl FontFamilyList {
- /// Return iterator of SingleFontFamily
- pub fn iter(&self) -> impl Iterator<Item = &SingleFontFamily> {
- self.list.iter()
- }
-
- /// If there's a generic font family on the list which is suitable for user
- /// font prioritization, then move it ahead of the other families in the list,
- /// except for any families known to be ligature-based icon fonts, where using a
- /// generic instead of the site's specified font may cause substantial breakage.
- /// If no suitable generic is found in the list, insert the default generic ahead
- /// of all the listed families except for known ligature-based icon fonts.
- #[cfg(feature = "gecko")]
- pub(crate) fn prioritize_first_generic_or_prepend(&mut self, generic: GenericFontFamily) {
- let mut index_of_first_generic = None;
- let mut target_index = None;
-
- for (i, f) in self.iter().enumerate() {
- match &*f {
- SingleFontFamily::Generic(f) => {
- if index_of_first_generic.is_none() && f.valid_for_user_font_prioritization() {
- // If we haven't found a target position, there's nothing to do;
- // this entry is already ahead of everything except any whitelisted
- // icon fonts.
- if target_index.is_none() {
- return;
- }
- index_of_first_generic = Some(i);
- break;
- }
- // A non-prioritized generic (e.g. cursive, fantasy) becomes the target
- // position for prioritization, just like arbitrary named families.
- if target_index.is_none() {
- target_index = Some(i);
- }
- },
- SingleFontFamily::FamilyName(fam) => {
- // Target position for the first generic is in front of the first
- // non-whitelisted icon font family we find.
- if target_index.is_none() && !fam.is_known_icon_font_family() {
- target_index = Some(i);
- }
- },
- }
- }
-
- let mut new_list = self.list.iter().cloned().collect::<Vec<_>>();
- let first_generic = match index_of_first_generic {
- Some(i) => new_list.remove(i),
- None => SingleFontFamily::Generic(generic),
- };
-
- if let Some(i) = target_index {
- new_list.insert(i, first_generic);
- } else {
- new_list.push(first_generic);
- }
- self.list = crate::ArcSlice::from_iter(new_list.into_iter());
- }
-
- /// Returns whether we need to prioritize user fonts.
- #[cfg(feature = "gecko")]
- pub(crate) fn needs_user_font_prioritization(&self) -> bool {
- self.iter().next().map_or(true, |f| match f {
- SingleFontFamily::Generic(f) => !f.valid_for_user_font_prioritization(),
- _ => true,
- })
- }
-
- /// Return the generic ID if it is a single generic font
- pub fn single_generic(&self) -> Option<GenericFontFamily> {
- let mut iter = self.iter();
- if let Some(SingleFontFamily::Generic(f)) = iter.next() {
- if iter.next().is_none() {
- return Some(*f);
- }
- }
- None
- }
-}
-
-/// Preserve the readability of text when font fallback occurs
-pub type FontSizeAdjust = generics::GenericFontSizeAdjust<NonNegativeNumber>;
-
-impl FontSizeAdjust {
- #[inline]
- /// Default value of font-size-adjust
- pub fn none() -> Self {
- FontSizeAdjust::None
- }
-}
-
-/// Use FontSettings as computed type of FontFeatureSettings.
-pub type FontFeatureSettings = FontSettings<FeatureTagValue<Integer>>;
-
-/// The computed value for font-variation-settings.
-pub type FontVariationSettings = FontSettings<VariationValue<Number>>;
-
-// The computed value of font-{feature,variation}-settings discards values
-// with duplicate tags, keeping only the last occurrence of each tag.
-fn dedup_font_settings<T>(settings_list: &mut Vec<T>)
-where
- T: TaggedFontValue,
-{
- if settings_list.len() > 1 {
- settings_list.sort_by_key(|k| k.tag().0);
- // dedup() keeps the first of any duplicates, but we want the last,
- // so we implement it manually here.
- let mut prev_tag = settings_list.last().unwrap().tag();
- for i in (0..settings_list.len() - 1).rev() {
- let cur_tag = settings_list[i].tag();
- if cur_tag == prev_tag {
- settings_list.remove(i);
- }
- prev_tag = cur_tag;
- }
- }
-}
-
-impl<T> ToComputedValue for FontSettings<T>
-where
- T: ToComputedValue,
- <T as ToComputedValue>::ComputedValue: TaggedFontValue,
-{
- type ComputedValue = FontSettings<T::ComputedValue>;
-
- fn to_computed_value(&self, context: &Context) -> Self::ComputedValue {
- let mut v = self
- .0
- .iter()
- .map(|item| item.to_computed_value(context))
- .collect::<Vec<_>>();
- dedup_font_settings(&mut v);
- FontSettings(v.into_boxed_slice())
- }
-
- fn from_computed_value(computed: &Self::ComputedValue) -> Self {
- Self(
- computed
- .0
- .iter()
- .map(T::from_computed_value)
- .collect::<Vec<_>>()
- .into_boxed_slice(),
- )
- }
-}
-
-/// font-language-override can only have a single 1-4 ASCII character
-/// OpenType "language system" tag, so we should be able to compute
-/// it and store it as a 32-bit integer
-/// (see http://www.microsoft.com/typography/otspec/languagetags.htm).
-#[derive(
- Clone,
- Copy,
- Debug,
- Eq,
- MallocSizeOf,
- PartialEq,
- SpecifiedValueInfo,
- ToComputedValue,
- ToResolvedValue,
- ToShmem,
-)]
-#[repr(C)]
-#[value_info(other_values = "normal")]
-pub struct FontLanguageOverride(pub u32);
-
-impl FontLanguageOverride {
- #[inline]
- /// Get computed default value of `font-language-override` with 0
- pub fn normal() -> FontLanguageOverride {
- FontLanguageOverride(0)
- }
-
- /// Returns this value as a `&str`, backed by `storage`.
- #[inline]
- pub(crate) fn to_str(self, storage: &mut [u8; 4]) -> &str {
- *storage = u32::to_be_bytes(self.0);
- // Safe because we ensure it's ASCII during parsing
- let slice = if cfg!(debug_assertions) {
- std::str::from_utf8(&storage[..]).unwrap()
- } else {
- unsafe { std::str::from_utf8_unchecked(&storage[..]) }
- };
- slice.trim_end()
- }
-
- /// Unsafe because `Self::to_str` requires the value to represent a UTF-8
- /// string.
- #[inline]
- pub unsafe fn from_u32(value: u32) -> Self {
- Self(value)
- }
-}
-
-impl ToCss for FontLanguageOverride {
- fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
- where
- W: fmt::Write,
- {
- if self.0 == 0 {
- return dest.write_str("normal");
- }
- self.to_str(&mut [0; 4]).to_css(dest)
- }
-}
-
-// FIXME(emilio): Make Gecko use the cbindgen'd fontLanguageOverride, then
-// remove this.
-#[cfg(feature = "gecko")]
-impl From<u32> for FontLanguageOverride {
- fn from(v: u32) -> Self {
- unsafe { Self::from_u32(v) }
- }
-}
-
-#[cfg(feature = "gecko")]
-impl From<FontLanguageOverride> for u32 {
- fn from(v: FontLanguageOverride) -> u32 {
- v.0
- }
-}
-
-impl ToComputedValue for specified::MozScriptMinSize {
- type ComputedValue = MozScriptMinSize;
-
- fn to_computed_value(&self, cx: &Context) -> MozScriptMinSize {
- // this value is used in the computation of font-size, so
- // we use the parent size
- let base_size = FontBaseSize::InheritedStyle;
- match self.0 {
- NoCalcLength::FontRelative(value) => value.to_computed_value(cx, base_size),
- NoCalcLength::ServoCharacterWidth(value) => {
- value.to_computed_value(base_size.resolve(cx).computed_size())
- },
- ref l => l.to_computed_value(cx),
- }
- }
-
- fn from_computed_value(other: &MozScriptMinSize) -> Self {
- specified::MozScriptMinSize(ToComputedValue::from_computed_value(other))
- }
-}
-
-/// The computed value of the math-depth property.
-pub type MathDepth = i8;
-
-#[cfg(feature = "gecko")]
-impl ToComputedValue for specified::MathDepth {
- type ComputedValue = MathDepth;
-
- fn to_computed_value(&self, cx: &Context) -> i8 {
- use crate::properties::longhands::math_style::SpecifiedValue as MathStyleValue;
- use std::{cmp, i8};
-
- let int = match *self {
- specified::MathDepth::AutoAdd => {
- let parent = cx.builder.get_parent_font().clone_math_depth() as i32;
- let style = cx.builder.get_parent_font().clone_math_style();
- if style == MathStyleValue::Compact {
- parent.saturating_add(1)
- } else {
- parent
- }
- },
- specified::MathDepth::Add(rel) => {
- let parent = cx.builder.get_parent_font().clone_math_depth();
- (parent as i32).saturating_add(rel.to_computed_value(cx))
- },
- specified::MathDepth::Absolute(abs) => abs.to_computed_value(cx),
- };
- cmp::min(int, i8::MAX as i32) as i8
- }
-
- fn from_computed_value(other: &i8) -> Self {
- let computed_value = *other as i32;
- specified::MathDepth::Absolute(SpecifiedInteger::from_computed_value(&computed_value))
- }
-}
-
-/// - Use a signed 8.8 fixed-point value (representable range -128.0..128)
-///
-/// Values of <angle> below -90 or above 90 not permitted, so we use out of
-/// range values to represent normal | oblique
-pub const FONT_STYLE_FRACTION_BITS: u16 = 8;
-
-/// This is an alias which is useful mostly as a cbindgen / C++ inference
-/// workaround.
-pub type FontStyleFixedPoint = FixedPoint<i16, FONT_STYLE_FRACTION_BITS>;
-
-/// The computed value of `font-style`.
-///
-/// - Define out of range values min value (-128.0) as meaning 'normal'
-/// - Define max value (127.99609375) as 'italic'
-/// - Other values represent 'oblique <angle>'
-/// - Note that 'oblique 0deg' is distinct from 'normal' (should it be?)
-///
-/// cbindgen:derive-lt
-/// cbindgen:derive-lte
-/// cbindgen:derive-gt
-/// cbindgen:derive-gte
-#[derive(
- Clone,
- ComputeSquaredDistance,
- Copy,
- Debug,
- Eq,
- Hash,
- MallocSizeOf,
- PartialEq,
- PartialOrd,
- ToResolvedValue,
-)]
-#[cfg_attr(feature = "servo", derive(Deserialize, Serialize))]
-#[repr(C)]
-pub struct FontStyle(FontStyleFixedPoint);
-
-impl FontStyle {
- /// The normal keyword.
- pub const NORMAL: FontStyle = FontStyle(FontStyleFixedPoint {
- value: 100 << FONT_STYLE_FRACTION_BITS,
- });
- /// The italic keyword.
- pub const ITALIC: FontStyle = FontStyle(FontStyleFixedPoint {
- value: 101 << FONT_STYLE_FRACTION_BITS,
- });
-
- /// The default angle for `font-style: oblique`.
- /// See also https://github.com/w3c/csswg-drafts/issues/2295
- pub const DEFAULT_OBLIQUE_DEGREES: i16 = 14;
-
- /// The `oblique` keyword with the default degrees.
- pub const OBLIQUE: FontStyle = FontStyle(FontStyleFixedPoint {
- value: Self::DEFAULT_OBLIQUE_DEGREES << FONT_STYLE_FRACTION_BITS,
- });
-
- /// The `normal` value.
- #[inline]
- pub fn normal() -> Self {
- Self::NORMAL
- }
-
- /// Returns the oblique angle for this style.
- pub fn oblique(degrees: f32) -> Self {
- Self(FixedPoint::from_float(
- degrees
- .max(specified::FONT_STYLE_OBLIQUE_MIN_ANGLE_DEGREES)
- .min(specified::FONT_STYLE_OBLIQUE_MAX_ANGLE_DEGREES),
- ))
- }
-
- /// Returns the oblique angle for this style.
- pub fn oblique_degrees(&self) -> f32 {
- debug_assert_ne!(*self, Self::NORMAL);
- debug_assert_ne!(*self, Self::ITALIC);
- self.0.to_float()
- }
-}
-
-impl ToCss for FontStyle {
- fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
- where
- W: fmt::Write,
- {
- if *self == Self::NORMAL {
- return dest.write_str("normal");
- }
- if *self == Self::ITALIC {
- return dest.write_str("italic");
- }
- if *self == Self::OBLIQUE {
- return dest.write_str("oblique");
- }
- dest.write_str("oblique ")?;
- let angle = Angle::from_degrees(self.oblique_degrees());
- angle.to_css(dest)?;
- Ok(())
- }
-}
-
-impl ToAnimatedValue for FontStyle {
- type AnimatedValue = generics::FontStyle<Angle>;
-
- #[inline]
- fn to_animated_value(self) -> Self::AnimatedValue {
- if self == Self::NORMAL {
- // This allows us to animate between normal and oblique values. Per spec,
- // https://drafts.csswg.org/css-fonts-4/#font-style-prop:
- // Animation type: by computed value type; 'normal' animates as 'oblique 0deg'
- return generics::FontStyle::Oblique(Angle::from_degrees(0.0));
- }
- if self == Self::ITALIC {
- return generics::FontStyle::Italic;
- }
- generics::FontStyle::Oblique(Angle::from_degrees(self.oblique_degrees()))
- }
-
- #[inline]
- fn from_animated_value(animated: Self::AnimatedValue) -> Self {
- match animated {
- generics::FontStyle::Normal => Self::NORMAL,
- generics::FontStyle::Italic => Self::ITALIC,
- generics::FontStyle::Oblique(ref angle) => {
- if angle.degrees() == 0.0 {
- // Reverse the conversion done in to_animated_value()
- Self::NORMAL
- } else {
- Self::oblique(angle.degrees())
- }
- },
- }
- }
-}
-
-/// font-stretch is a percentage relative to normal.
-///
-/// We use an unsigned 10.6 fixed-point value (range 0.0 - 1023.984375)
-///
-/// We arbitrarily limit here to 1000%. (If that becomes a problem, we could
-/// reduce the number of fractional bits and increase the limit.)
-pub const FONT_STRETCH_FRACTION_BITS: u16 = 6;
-
-/// This is an alias which is useful mostly as a cbindgen / C++ inference
-/// workaround.
-pub type FontStretchFixedPoint = FixedPoint<u16, FONT_STRETCH_FRACTION_BITS>;
-
-/// A value for the font-stretch property per:
-///
-/// https://drafts.csswg.org/css-fonts-4/#propdef-font-stretch
-///
-/// cbindgen:derive-lt
-/// cbindgen:derive-lte
-/// cbindgen:derive-gt
-/// cbindgen:derive-gte
-#[derive(
- Clone, ComputeSquaredDistance, Copy, Debug, MallocSizeOf, PartialEq, PartialOrd, ToResolvedValue,
-)]
-#[cfg_attr(feature = "servo", derive(Deserialize, Hash, Serialize))]
-#[repr(C)]
-pub struct FontStretch(pub FontStretchFixedPoint);
-
-impl FontStretch {
- /// The fraction bits, as an easy-to-access-constant.
- pub const FRACTION_BITS: u16 = FONT_STRETCH_FRACTION_BITS;
- /// 0.5 in our floating point representation.
- pub const HALF: u16 = 1 << (Self::FRACTION_BITS - 1);
-
- /// The `ultra-condensed` keyword.
- pub const ULTRA_CONDENSED: FontStretch = FontStretch(FontStretchFixedPoint {
- value: 50 << Self::FRACTION_BITS,
- });
- /// The `extra-condensed` keyword.
- pub const EXTRA_CONDENSED: FontStretch = FontStretch(FontStretchFixedPoint {
- value: (62 << Self::FRACTION_BITS) + Self::HALF,
- });
- /// The `condensed` keyword.
- pub const CONDENSED: FontStretch = FontStretch(FontStretchFixedPoint {
- value: 75 << Self::FRACTION_BITS,
- });
- /// The `semi-condensed` keyword.
- pub const SEMI_CONDENSED: FontStretch = FontStretch(FontStretchFixedPoint {
- value: (87 << Self::FRACTION_BITS) + Self::HALF,
- });
- /// The `normal` keyword.
- pub const NORMAL: FontStretch = FontStretch(FontStretchFixedPoint {
- value: 100 << Self::FRACTION_BITS,
- });
- /// The `semi-expanded` keyword.
- pub const SEMI_EXPANDED: FontStretch = FontStretch(FontStretchFixedPoint {
- value: (112 << Self::FRACTION_BITS) + Self::HALF,
- });
- /// The `expanded` keyword.
- pub const EXPANDED: FontStretch = FontStretch(FontStretchFixedPoint {
- value: 125 << Self::FRACTION_BITS,
- });
- /// The `extra-expanded` keyword.
- pub const EXTRA_EXPANDED: FontStretch = FontStretch(FontStretchFixedPoint {
- value: 150 << Self::FRACTION_BITS,
- });
- /// The `ultra-expanded` keyword.
- pub const ULTRA_EXPANDED: FontStretch = FontStretch(FontStretchFixedPoint {
- value: 200 << Self::FRACTION_BITS,
- });
-
- /// 100%
- pub fn hundred() -> Self {
- Self::NORMAL
- }
-
- /// Converts to a computed percentage.
- #[inline]
- pub fn to_percentage(&self) -> Percentage {
- Percentage(self.0.to_float() / 100.0)
- }
-
- /// Converts from a computed percentage value.
- pub fn from_percentage(p: f32) -> Self {
- Self(FixedPoint::from_float((p * 100.).max(0.0).min(1000.0)))
- }
-
- /// Returns a relevant stretch value from a keyword.
- /// https://drafts.csswg.org/css-fonts-4/#font-stretch-prop
- pub fn from_keyword(kw: specified::FontStretchKeyword) -> Self {
- use specified::FontStretchKeyword::*;
- match kw {
- UltraCondensed => Self::ULTRA_CONDENSED,
- ExtraCondensed => Self::EXTRA_CONDENSED,
- Condensed => Self::CONDENSED,
- SemiCondensed => Self::SEMI_CONDENSED,
- Normal => Self::NORMAL,
- SemiExpanded => Self::SEMI_EXPANDED,
- Expanded => Self::EXPANDED,
- ExtraExpanded => Self::EXTRA_EXPANDED,
- UltraExpanded => Self::ULTRA_EXPANDED,
- }
- }
-
- /// Returns the stretch keyword if we map to one of the relevant values.
- pub fn as_keyword(&self) -> Option<specified::FontStretchKeyword> {
- use specified::FontStretchKeyword::*;
- // TODO: Can we use match here?
- if *self == Self::ULTRA_CONDENSED {
- return Some(UltraCondensed);
- }
- if *self == Self::EXTRA_CONDENSED {
- return Some(ExtraCondensed);
- }
- if *self == Self::CONDENSED {
- return Some(Condensed);
- }
- if *self == Self::SEMI_CONDENSED {
- return Some(SemiCondensed);
- }
- if *self == Self::NORMAL {
- return Some(Normal);
- }
- if *self == Self::SEMI_EXPANDED {
- return Some(SemiExpanded);
- }
- if *self == Self::EXPANDED {
- return Some(Expanded);
- }
- if *self == Self::EXTRA_EXPANDED {
- return Some(ExtraExpanded);
- }
- if *self == Self::ULTRA_EXPANDED {
- return Some(UltraExpanded);
- }
- None
- }
-}
-
-impl ToCss for FontStretch {
- fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
- where
- W: fmt::Write,
- {
- self.to_percentage().to_css(dest)
- }
-}
-
-impl ToAnimatedValue for FontStretch {
- type AnimatedValue = Percentage;
-
- #[inline]
- fn to_animated_value(self) -> Self::AnimatedValue {
- self.to_percentage()
- }
-
- #[inline]
- fn from_animated_value(animated: Self::AnimatedValue) -> Self {
- Self::from_percentage(animated.0)
- }
-}
diff --git a/components/style/values/computed/image.rs b/components/style/values/computed/image.rs
deleted file mode 100644
index 0f8a11f9197..00000000000
--- a/components/style/values/computed/image.rs
+++ /dev/null
@@ -1,213 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
-
-//! CSS handling for the computed value of
-//! [`image`][image]s
-//!
-//! [image]: https://drafts.csswg.org/css-images/#image-values
-
-use crate::values::computed::percentage::Percentage;
-use crate::values::computed::position::Position;
-use crate::values::computed::url::ComputedImageUrl;
-#[cfg(feature = "gecko")]
-use crate::values::computed::NumberOrPercentage;
-use crate::values::computed::{Angle, Color, Context};
-use crate::values::computed::{
- AngleOrPercentage, LengthPercentage, NonNegativeLength, NonNegativeLengthPercentage,
- Resolution, ToComputedValue,
-};
-use crate::values::generics::image::{self as generic, GradientCompatMode};
-use crate::values::specified::image as specified;
-use crate::values::specified::position::{HorizontalPositionKeyword, VerticalPositionKeyword};
-use std::f32::consts::PI;
-use std::fmt::{self, Write};
-use style_traits::{CssWriter, ToCss};
-
-pub use specified::ImageRendering;
-
-/// Computed values for an image according to CSS-IMAGES.
-/// <https://drafts.csswg.org/css-images/#image-values>
-pub type Image =
- generic::GenericImage<Gradient, MozImageRect, ComputedImageUrl, Color, Percentage, Resolution>;
-
-// Images should remain small, see https://github.com/servo/servo/pull/18430
-size_of_test!(Image, 40);
-
-/// Computed values for a CSS gradient.
-/// <https://drafts.csswg.org/css-images/#gradients>
-pub type Gradient = generic::GenericGradient<
- LineDirection,
- LengthPercentage,
- NonNegativeLength,
- NonNegativeLengthPercentage,
- Position,
- Angle,
- AngleOrPercentage,
- Color,
->;
-
-/// Computed values for CSS cross-fade
-/// <https://drafts.csswg.org/css-images-4/#cross-fade-function>
-pub type CrossFade = generic::CrossFade<Image, Color, Percentage>;
-
-/// A computed radial gradient ending shape.
-pub type EndingShape = generic::GenericEndingShape<NonNegativeLength, NonNegativeLengthPercentage>;
-
-/// A computed gradient line direction.
-#[derive(Clone, Copy, Debug, MallocSizeOf, PartialEq, ToResolvedValue)]
-#[repr(C, u8)]
-pub enum LineDirection {
- /// An angle.
- Angle(Angle),
- /// A horizontal direction.
- Horizontal(HorizontalPositionKeyword),
- /// A vertical direction.
- Vertical(VerticalPositionKeyword),
- /// A corner.
- Corner(HorizontalPositionKeyword, VerticalPositionKeyword),
-}
-
-/// The computed value for an `image-set()` image.
-pub type ImageSet = generic::GenericImageSet<Image, Resolution>;
-
-impl ToComputedValue for specified::ImageSet {
- type ComputedValue = ImageSet;
-
- fn to_computed_value(&self, context: &Context) -> Self::ComputedValue {
- let items = self.items.to_computed_value(context);
- let dpr = context.device().device_pixel_ratio().get();
-
- let mut supported_image = false;
- let mut selected_index = std::usize::MAX;
- let mut selected_resolution = 0.0;
-
- for (i, item) in items.iter().enumerate() {
- if item.has_mime_type && !context.device().is_supported_mime_type(&item.mime_type) {
- // If the MIME type is not supported, we discard the ImageSetItem.
- continue;
- }
-
- let candidate_resolution = item.resolution.dppx();
- debug_assert!(candidate_resolution >= 0.0, "Resolutions should be non-negative");
- if candidate_resolution == 0.0 {
- // If the resolution is 0, we also treat it as an invalid image.
- continue;
- }
-
- // https://drafts.csswg.org/css-images-4/#image-set-notation:
- //
- // Make a UA-specific choice of which to load, based on whatever criteria deemed
- // relevant (such as the resolution of the display, connection speed, etc).
- //
- // For now, select the lowest resolution greater than display density, otherwise the
- // greatest resolution available.
- let better_candidate = || {
- if selected_resolution < dpr && candidate_resolution > selected_resolution {
- return true;
- }
- if candidate_resolution < selected_resolution && candidate_resolution >= dpr {
- return true;
- }
- false
- };
-
- // The first item with a supported MIME type is obviously the current best candidate
- if !supported_image || better_candidate() {
- supported_image = true;
- selected_index = i;
- selected_resolution = candidate_resolution;
- }
- }
-
- ImageSet {
- selected_index,
- items,
- }
- }
-
- fn from_computed_value(computed: &Self::ComputedValue) -> Self {
- Self {
- selected_index: std::usize::MAX,
- items: ToComputedValue::from_computed_value(&computed.items),
- }
- }
-}
-
-/// Computed values for `-moz-image-rect(...)`.
-#[cfg(feature = "gecko")]
-pub type MozImageRect = generic::GenericMozImageRect<NumberOrPercentage, ComputedImageUrl>;
-
-/// Empty enum on non-gecko
-#[cfg(not(feature = "gecko"))]
-pub type MozImageRect = specified::MozImageRect;
-
-impl generic::LineDirection for LineDirection {
- fn points_downwards(&self, compat_mode: GradientCompatMode) -> bool {
- match *self {
- LineDirection::Angle(angle) => angle.radians() == PI,
- LineDirection::Vertical(VerticalPositionKeyword::Bottom) => {
- compat_mode == GradientCompatMode::Modern
- },
- LineDirection::Vertical(VerticalPositionKeyword::Top) => {
- compat_mode != GradientCompatMode::Modern
- },
- _ => false,
- }
- }
-
- fn to_css<W>(&self, dest: &mut CssWriter<W>, compat_mode: GradientCompatMode) -> fmt::Result
- where
- W: Write,
- {
- match *self {
- LineDirection::Angle(ref angle) => angle.to_css(dest),
- LineDirection::Horizontal(x) => {
- if compat_mode == GradientCompatMode::Modern {
- dest.write_str("to ")?;
- }
- x.to_css(dest)
- },
- LineDirection::Vertical(y) => {
- if compat_mode == GradientCompatMode::Modern {
- dest.write_str("to ")?;
- }
- y.to_css(dest)
- },
- LineDirection::Corner(x, y) => {
- if compat_mode == GradientCompatMode::Modern {
- dest.write_str("to ")?;
- }
- x.to_css(dest)?;
- dest.write_char(' ')?;
- y.to_css(dest)
- },
- }
- }
-}
-
-impl ToComputedValue for specified::LineDirection {
- type ComputedValue = LineDirection;
-
- fn to_computed_value(&self, context: &Context) -> Self::ComputedValue {
- match *self {
- specified::LineDirection::Angle(ref angle) => {
- LineDirection::Angle(angle.to_computed_value(context))
- },
- specified::LineDirection::Horizontal(x) => LineDirection::Horizontal(x),
- specified::LineDirection::Vertical(y) => LineDirection::Vertical(y),
- specified::LineDirection::Corner(x, y) => LineDirection::Corner(x, y),
- }
- }
-
- fn from_computed_value(computed: &Self::ComputedValue) -> Self {
- match *computed {
- LineDirection::Angle(ref angle) => {
- specified::LineDirection::Angle(ToComputedValue::from_computed_value(angle))
- },
- LineDirection::Horizontal(x) => specified::LineDirection::Horizontal(x),
- LineDirection::Vertical(y) => specified::LineDirection::Vertical(y),
- LineDirection::Corner(x, y) => specified::LineDirection::Corner(x, y),
- }
- }
-}
diff --git a/components/style/values/computed/length.rs b/components/style/values/computed/length.rs
deleted file mode 100644
index 64285e7c124..00000000000
--- a/components/style/values/computed/length.rs
+++ /dev/null
@@ -1,522 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
-
-//! `<length>` computed values, and related ones.
-
-use super::{Context, Number, ToComputedValue};
-use crate::values::animated::ToAnimatedValue;
-use crate::values::computed::NonNegativeNumber;
-use crate::values::generics::length as generics;
-use crate::values::generics::length::{
- GenericLengthOrNumber, GenericLengthPercentageOrNormal, GenericMaxSize, GenericSize,
-};
-use crate::values::generics::NonNegative;
-use crate::values::specified::length::{AbsoluteLength, FontBaseSize};
-use crate::values::{specified, CSSFloat};
-use crate::Zero;
-use app_units::Au;
-use std::fmt::{self, Write};
-use std::ops::{Add, AddAssign, Div, Mul, MulAssign, Neg, Sub, SubAssign};
-use style_traits::{CSSPixel, CssWriter, ToCss};
-
-pub use super::image::Image;
-pub use super::length_percentage::{LengthPercentage, NonNegativeLengthPercentage};
-pub use crate::values::specified::url::UrlOrNone;
-pub use crate::values::specified::{Angle, BorderStyle, Time};
-
-impl ToComputedValue for specified::NoCalcLength {
- type ComputedValue = Length;
-
- #[inline]
- fn to_computed_value(&self, context: &Context) -> Self::ComputedValue {
- self.to_computed_value_with_base_size(context, FontBaseSize::CurrentStyle)
- }
-
- #[inline]
- fn from_computed_value(computed: &Self::ComputedValue) -> Self {
- Self::Absolute(AbsoluteLength::Px(computed.px()))
- }
-}
-
-impl specified::NoCalcLength {
- /// Computes a length with a given font-relative base size.
- pub fn to_computed_value_with_base_size(
- &self,
- context: &Context,
- base_size: FontBaseSize,
- ) -> Length {
- match *self {
- Self::Absolute(length) => length.to_computed_value(context),
- Self::FontRelative(length) => length.to_computed_value(context, base_size),
- Self::ViewportPercentage(length) => length.to_computed_value(context),
- Self::ContainerRelative(length) => length.to_computed_value(context),
- Self::ServoCharacterWidth(length) => length
- .to_computed_value(context.style().get_font().clone_font_size().computed_size()),
- }
- }
-}
-
-impl ToComputedValue for specified::Length {
- type ComputedValue = Length;
-
- #[inline]
- fn to_computed_value(&self, context: &Context) -> Self::ComputedValue {
- match *self {
- Self::NoCalc(l) => l.to_computed_value(context),
- Self::Calc(ref calc) => calc.to_computed_value(context).to_length().unwrap(),
- }
- }
-
- #[inline]
- fn from_computed_value(computed: &Self::ComputedValue) -> Self {
- Self::NoCalc(specified::NoCalcLength::from_computed_value(computed))
- }
-}
-
-/// Some boilerplate to share between negative and non-negative
-/// length-percentage or auto.
-macro_rules! computed_length_percentage_or_auto {
- ($inner:ty) => {
- /// Returns the used value.
- #[inline]
- pub fn to_used_value(&self, percentage_basis: Au) -> Option<Au> {
- match *self {
- Self::Auto => None,
- Self::LengthPercentage(ref lp) => Some(lp.to_used_value(percentage_basis)),
- }
- }
-
- /// Returns true if the computed value is absolute 0 or 0%.
- #[inline]
- pub fn is_definitely_zero(&self) -> bool {
- use crate::values::generics::length::LengthPercentageOrAuto::*;
- match *self {
- LengthPercentage(ref l) => l.is_definitely_zero(),
- Auto => false,
- }
- }
- };
-}
-
-/// A computed type for `<length-percentage> | auto`.
-pub type LengthPercentageOrAuto = generics::GenericLengthPercentageOrAuto<LengthPercentage>;
-
-impl LengthPercentageOrAuto {
- /// Clamps the value to a non-negative value.
- pub fn clamp_to_non_negative(self) -> Self {
- use crate::values::generics::length::LengthPercentageOrAuto::*;
- match self {
- LengthPercentage(l) => LengthPercentage(l.clamp_to_non_negative()),
- Auto => Auto,
- }
- }
-
- /// Convert to have a borrow inside the enum
- pub fn as_ref(&self) -> generics::GenericLengthPercentageOrAuto<&LengthPercentage> {
- use crate::values::generics::length::LengthPercentageOrAuto::*;
- match *self {
- LengthPercentage(ref lp) => LengthPercentage(lp),
- Auto => Auto,
- }
- }
-
- computed_length_percentage_or_auto!(LengthPercentage);
-}
-
-impl generics::GenericLengthPercentageOrAuto<&LengthPercentage> {
- /// Resolves the percentage.
- #[inline]
- pub fn percentage_relative_to(&self, basis: Length) -> LengthOrAuto {
- use crate::values::generics::length::LengthPercentageOrAuto::*;
- match self {
- LengthPercentage(length_percentage) => {
- LengthPercentage(length_percentage.percentage_relative_to(basis))
- },
- Auto => Auto,
- }
- }
-
- /// Maybe resolves the percentage.
- #[inline]
- pub fn maybe_percentage_relative_to(&self, basis: Option<Length>) -> LengthOrAuto {
- use crate::values::generics::length::LengthPercentageOrAuto::*;
- match self {
- LengthPercentage(length_percentage) => length_percentage
- .maybe_percentage_relative_to(basis)
- .map_or(Auto, LengthPercentage),
- Auto => Auto,
- }
- }
-}
-
-/// A wrapper of LengthPercentageOrAuto, whose value must be >= 0.
-pub type NonNegativeLengthPercentageOrAuto =
- generics::GenericLengthPercentageOrAuto<NonNegativeLengthPercentage>;
-
-impl NonNegativeLengthPercentageOrAuto {
- computed_length_percentage_or_auto!(NonNegativeLengthPercentage);
-}
-
-#[cfg(feature = "servo")]
-impl MaxSize {
- /// Convert the computed value into used value.
- #[inline]
- pub fn to_used_value(&self, percentage_basis: Au) -> Option<Au> {
- match *self {
- GenericMaxSize::None => None,
- GenericMaxSize::LengthPercentage(ref lp) => Some(lp.to_used_value(percentage_basis)),
- }
- }
-}
-
-impl Size {
- /// Convert the computed value into used value.
- #[inline]
- #[cfg(feature = "servo")]
- pub fn to_used_value(&self, percentage_basis: Au) -> Option<Au> {
- match *self {
- GenericSize::Auto => None,
- GenericSize::LengthPercentage(ref lp) => Some(lp.to_used_value(percentage_basis)),
- }
- }
-
- /// Returns true if the computed value is absolute 0 or 0%.
- #[inline]
- pub fn is_definitely_zero(&self) -> bool {
- match *self {
- Self::Auto => false,
- Self::LengthPercentage(ref lp) => lp.is_definitely_zero(),
- #[cfg(feature = "gecko")]
- Self::MinContent |
- Self::MaxContent |
- Self::FitContent |
- Self::MozAvailable |
- Self::FitContentFunction(_) => false,
- }
- }
-}
-
-/// The computed `<length>` value.
-#[derive(
- Animate,
- Clone,
- ComputeSquaredDistance,
- Copy,
- Deserialize,
- MallocSizeOf,
- PartialEq,
- PartialOrd,
- Serialize,
- ToAnimatedValue,
- ToAnimatedZero,
- ToComputedValue,
- ToResolvedValue,
- ToShmem,
-)]
-#[repr(C)]
-pub struct CSSPixelLength(CSSFloat);
-
-impl fmt::Debug for CSSPixelLength {
- fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
- self.0.fmt(f)?;
- f.write_str(" px")
- }
-}
-
-impl CSSPixelLength {
- /// Return a new CSSPixelLength.
- #[inline]
- pub fn new(px: CSSFloat) -> Self {
- CSSPixelLength(px)
- }
-
- /// Returns a normalized (NaN turned to zero) version of this length.
- #[inline]
- pub fn normalized(self) -> Self {
- Self::new(crate::values::normalize(self.0))
- }
-
- /// Returns a finite (normalized and clamped to float min and max) version of this length.
- #[inline]
- pub fn finite(self) -> Self {
- Self::new(crate::values::normalize(self.0).min(f32::MAX).max(f32::MIN))
- }
-
- /// Scale the length by a given amount.
- #[inline]
- pub fn scale_by(self, scale: CSSFloat) -> Self {
- CSSPixelLength(self.0 * scale)
- }
-
- /// Return the containing pixel value.
- #[inline]
- pub fn px(self) -> CSSFloat {
- self.0
- }
-
- /// Return the length with app_unit i32 type.
- #[inline]
- pub fn to_i32_au(self) -> i32 {
- Au::from(self).0
- }
-
- /// Return the absolute value of this length.
- #[inline]
- pub fn abs(self) -> Self {
- CSSPixelLength::new(self.0.abs())
- }
-
- /// Return the clamped value of this length.
- #[inline]
- pub fn clamp_to_non_negative(self) -> Self {
- CSSPixelLength::new(self.0.max(0.))
- }
-
- /// Returns the minimum between `self` and `other`.
- #[inline]
- pub fn min(self, other: Self) -> Self {
- CSSPixelLength::new(self.0.min(other.0))
- }
-
- /// Returns the maximum between `self` and `other`.
- #[inline]
- pub fn max(self, other: Self) -> Self {
- CSSPixelLength::new(self.0.max(other.0))
- }
-
- /// Sets `self` to the maximum between `self` and `other`.
- #[inline]
- pub fn max_assign(&mut self, other: Self) {
- *self = self.max(other);
- }
-
- /// Clamp the value to a lower bound and an optional upper bound.
- ///
- /// Can be used for example with `min-width` and `max-width`.
- #[inline]
- pub fn clamp_between_extremums(self, min_size: Self, max_size: Option<Self>) -> Self {
- self.clamp_below_max(max_size).max(min_size)
- }
-
- /// Clamp the value to an optional upper bound.
- ///
- /// Can be used for example with `max-width`.
- #[inline]
- pub fn clamp_below_max(self, max_size: Option<Self>) -> Self {
- match max_size {
- None => self,
- Some(max_size) => self.min(max_size),
- }
- }
-}
-
-impl num_traits::Zero for CSSPixelLength {
- fn zero() -> Self {
- CSSPixelLength::new(0.)
- }
-
- fn is_zero(&self) -> bool {
- self.px() == 0.
- }
-}
-
-impl ToCss for CSSPixelLength {
- #[inline]
- fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
- where
- W: Write,
- {
- self.0.to_css(dest)?;
- dest.write_str("px")
- }
-}
-
-impl std::iter::Sum for CSSPixelLength {
- fn sum<I: Iterator<Item = Self>>(iter: I) -> Self {
- iter.fold(Length::zero(), Add::add)
- }
-}
-
-impl Add for CSSPixelLength {
- type Output = Self;
-
- #[inline]
- fn add(self, other: Self) -> Self {
- Self::new(self.px() + other.px())
- }
-}
-
-impl AddAssign for CSSPixelLength {
- #[inline]
- fn add_assign(&mut self, other: Self) {
- self.0 += other.0;
- }
-}
-
-impl Div for CSSPixelLength {
- type Output = CSSFloat;
-
- #[inline]
- fn div(self, other: Self) -> CSSFloat {
- self.px() / other.px()
- }
-}
-
-impl Div<CSSFloat> for CSSPixelLength {
- type Output = Self;
-
- #[inline]
- fn div(self, other: CSSFloat) -> Self {
- Self::new(self.px() / other)
- }
-}
-
-impl MulAssign<CSSFloat> for CSSPixelLength {
- #[inline]
- fn mul_assign(&mut self, other: CSSFloat) {
- self.0 *= other;
- }
-}
-
-impl Mul<CSSFloat> for CSSPixelLength {
- type Output = Self;
-
- #[inline]
- fn mul(self, other: CSSFloat) -> Self {
- Self::new(self.px() * other)
- }
-}
-
-impl Neg for CSSPixelLength {
- type Output = Self;
-
- #[inline]
- fn neg(self) -> Self {
- CSSPixelLength::new(-self.0)
- }
-}
-
-impl Sub for CSSPixelLength {
- type Output = Self;
-
- #[inline]
- fn sub(self, other: Self) -> Self {
- Self::new(self.px() - other.px())
- }
-}
-
-impl SubAssign for CSSPixelLength {
- #[inline]
- fn sub_assign(&mut self, other: Self) {
- self.0 -= other.0;
- }
-}
-
-impl From<CSSPixelLength> for Au {
- #[inline]
- fn from(len: CSSPixelLength) -> Self {
- Au::from_f32_px(len.0)
- }
-}
-
-impl From<Au> for CSSPixelLength {
- #[inline]
- fn from(len: Au) -> Self {
- CSSPixelLength::new(len.to_f32_px())
- }
-}
-
-impl From<CSSPixelLength> for euclid::Length<CSSFloat, CSSPixel> {
- #[inline]
- fn from(length: CSSPixelLength) -> Self {
- Self::new(length.0)
- }
-}
-
-/// An alias of computed `<length>` value.
-pub type Length = CSSPixelLength;
-
-/// Either a computed `<length>` or the `auto` keyword.
-pub type LengthOrAuto = generics::GenericLengthPercentageOrAuto<Length>;
-
-/// Either a non-negative `<length>` or the `auto` keyword.
-pub type NonNegativeLengthOrAuto = generics::GenericLengthPercentageOrAuto<NonNegativeLength>;
-
-/// Either a computed `<length>` or a `<number>` value.
-pub type LengthOrNumber = GenericLengthOrNumber<Length, Number>;
-
-/// A wrapper of Length, whose value must be >= 0.
-pub type NonNegativeLength = NonNegative<Length>;
-
-impl ToAnimatedValue for NonNegativeLength {
- type AnimatedValue = Length;
-
- #[inline]
- fn to_animated_value(self) -> Self::AnimatedValue {
- self.0
- }
-
- #[inline]
- fn from_animated_value(animated: Self::AnimatedValue) -> Self {
- NonNegativeLength::new(animated.px().max(0.))
- }
-}
-
-impl NonNegativeLength {
- /// Create a NonNegativeLength.
- #[inline]
- pub fn new(px: CSSFloat) -> Self {
- NonNegative(Length::new(px.max(0.)))
- }
-
- /// Return the pixel value of |NonNegativeLength|.
- #[inline]
- pub fn px(&self) -> CSSFloat {
- self.0.px()
- }
-
- #[inline]
- /// Ensures it is non negative
- pub fn clamp(self) -> Self {
- if (self.0).0 < 0. {
- Self::zero()
- } else {
- self
- }
- }
-}
-
-impl From<Length> for NonNegativeLength {
- #[inline]
- fn from(len: Length) -> Self {
- NonNegative(len)
- }
-}
-
-impl From<Au> for NonNegativeLength {
- #[inline]
- fn from(au: Au) -> Self {
- NonNegative(au.into())
- }
-}
-
-impl From<NonNegativeLength> for Au {
- #[inline]
- fn from(non_negative_len: NonNegativeLength) -> Self {
- Au::from(non_negative_len.0)
- }
-}
-
-/// Either a computed NonNegativeLengthPercentage or the `normal` keyword.
-pub type NonNegativeLengthPercentageOrNormal =
- GenericLengthPercentageOrNormal<NonNegativeLengthPercentage>;
-
-/// Either a non-negative `<length>` or a `<number>`.
-pub type NonNegativeLengthOrNumber = GenericLengthOrNumber<NonNegativeLength, NonNegativeNumber>;
-
-/// A computed value for `min-width`, `min-height`, `width` or `height` property.
-pub type Size = GenericSize<NonNegativeLengthPercentage>;
-
-/// A computed value for `max-width` or `min-height` property.
-pub type MaxSize = GenericMaxSize<NonNegativeLengthPercentage>;
diff --git a/components/style/values/computed/length_percentage.rs b/components/style/values/computed/length_percentage.rs
deleted file mode 100644
index a8f5868b997..00000000000
--- a/components/style/values/computed/length_percentage.rs
+++ /dev/null
@@ -1,899 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
-
-//! `<length-percentage>` computed values, and related ones.
-//!
-//! The over-all design is a tagged pointer, with the lower bits of the pointer
-//! being non-zero if it is a non-calc value.
-//!
-//! It is expected to take 64 bits both in x86 and x86-64. This is implemented
-//! as a `union`, with 4 different variants:
-//!
-//! * The length and percentage variants have a { tag, f32 } (effectively)
-//! layout. The tag has to overlap with the lower 2 bits of the calc variant.
-//!
-//! * The `calc()` variant is a { tag, pointer } in x86 (so same as the
-//! others), or just a { pointer } in x86-64 (so that the two bits of the tag
-//! can be obtained from the lower bits of the pointer).
-//!
-//! * There's a `tag` variant just to make clear when only the tag is intended
-//! to be read. Note that the tag needs to be masked always by `TAG_MASK`, to
-//! deal with the pointer variant in x86-64.
-//!
-//! The assertions in the constructor methods ensure that the tag getter matches
-//! our expectations.
-
-use super::{Context, Length, Percentage, ToComputedValue};
-use crate::values::animated::{Animate, Procedure, ToAnimatedValue, ToAnimatedZero};
-use crate::values::distance::{ComputeSquaredDistance, SquaredDistance};
-use crate::values::generics::{calc, NonNegative};
-use crate::values::specified::length::FontBaseSize;
-use crate::values::{specified, CSSFloat};
-use crate::{Zero, ZeroNoPercent};
-use app_units::Au;
-use malloc_size_of::{MallocSizeOf, MallocSizeOfOps};
-use serde::{Deserialize, Serialize};
-use std::borrow::Cow;
-use std::fmt::{self, Write};
-use style_traits::values::specified::AllowedNumericType;
-use style_traits::{CssWriter, ToCss};
-
-#[doc(hidden)]
-#[derive(Clone, Copy)]
-#[repr(C)]
-pub struct LengthVariant {
- tag: u8,
- length: Length,
-}
-
-#[doc(hidden)]
-#[derive(Clone, Copy)]
-#[repr(C)]
-pub struct PercentageVariant {
- tag: u8,
- percentage: Percentage,
-}
-
-// NOTE(emilio): cbindgen only understands the #[cfg] on the top level
-// definition.
-#[doc(hidden)]
-#[derive(Clone, Copy)]
-#[repr(C)]
-#[cfg(target_pointer_width = "32")]
-pub struct CalcVariant {
- tag: u8,
- ptr: *mut CalcLengthPercentage,
-}
-
-#[doc(hidden)]
-#[derive(Clone, Copy)]
-#[repr(C)]
-#[cfg(target_pointer_width = "64")]
-pub struct CalcVariant {
- ptr: usize, // In little-endian byte order
-}
-
-// `CalcLengthPercentage` is `Send + Sync` as asserted below.
-unsafe impl Send for CalcVariant {}
-unsafe impl Sync for CalcVariant {}
-
-#[doc(hidden)]
-#[derive(Clone, Copy)]
-#[repr(C)]
-pub struct TagVariant {
- tag: u8,
-}
-
-/// A `<length-percentage>` value. This can be either a `<length>`, a
-/// `<percentage>`, or a combination of both via `calc()`.
-///
-/// cbindgen:private-default-tagged-enum-constructor=false
-/// cbindgen:derive-mut-casts=true
-///
-/// https://drafts.csswg.org/css-values-4/#typedef-length-percentage
-///
-/// The tag is stored in the lower two bits.
-///
-/// We need to use a struct instead of the union directly because unions with
-/// Drop implementations are unstable, looks like.
-///
-/// Also we need the union and the variants to be `pub` (even though the member
-/// is private) so that cbindgen generates it. They're not part of the public
-/// API otherwise.
-#[repr(transparent)]
-pub struct LengthPercentage(LengthPercentageUnion);
-
-#[doc(hidden)]
-#[repr(C)]
-pub union LengthPercentageUnion {
- length: LengthVariant,
- percentage: PercentageVariant,
- calc: CalcVariant,
- tag: TagVariant,
-}
-
-impl LengthPercentageUnion {
- #[doc(hidden)] // Need to be public so that cbindgen generates it.
- pub const TAG_CALC: u8 = 0;
- #[doc(hidden)]
- pub const TAG_LENGTH: u8 = 1;
- #[doc(hidden)]
- pub const TAG_PERCENTAGE: u8 = 2;
- #[doc(hidden)]
- pub const TAG_MASK: u8 = 0b11;
-}
-
-#[derive(Clone, Copy, Debug, PartialEq)]
-#[repr(u8)]
-enum Tag {
- Calc = LengthPercentageUnion::TAG_CALC,
- Length = LengthPercentageUnion::TAG_LENGTH,
- Percentage = LengthPercentageUnion::TAG_PERCENTAGE,
-}
-
-// All the members should be 64 bits, even in 32-bit builds.
-#[allow(unused)]
-unsafe fn static_assert() {
- fn assert_send_and_sync<T: Send + Sync>() {}
- std::mem::transmute::<u64, LengthVariant>(0u64);
- std::mem::transmute::<u64, PercentageVariant>(0u64);
- std::mem::transmute::<u64, CalcVariant>(0u64);
- std::mem::transmute::<u64, LengthPercentage>(0u64);
- assert_send_and_sync::<LengthVariant>();
- assert_send_and_sync::<PercentageVariant>();
- assert_send_and_sync::<CalcLengthPercentage>();
-}
-
-impl Drop for LengthPercentage {
- fn drop(&mut self) {
- if self.tag() == Tag::Calc {
- let _ = unsafe { Box::from_raw(self.calc_ptr()) };
- }
- }
-}
-
-impl MallocSizeOf for LengthPercentage {
- fn size_of(&self, ops: &mut MallocSizeOfOps) -> usize {
- match self.unpack() {
- Unpacked::Length(..) | Unpacked::Percentage(..) => 0,
- Unpacked::Calc(c) => unsafe { ops.malloc_size_of(c) },
- }
- }
-}
-
-/// An unpacked `<length-percentage>` that borrows the `calc()` variant.
-#[derive(Clone, Debug, PartialEq, ToCss)]
-enum Unpacked<'a> {
- Calc(&'a CalcLengthPercentage),
- Length(Length),
- Percentage(Percentage),
-}
-
-/// An unpacked `<length-percentage>` that mutably borrows the `calc()` variant.
-enum UnpackedMut<'a> {
- Calc(&'a mut CalcLengthPercentage),
- Length(Length),
- Percentage(Percentage),
-}
-
-/// An unpacked `<length-percentage>` that owns the `calc()` variant, for
-/// serialization purposes.
-#[derive(Deserialize, PartialEq, Serialize)]
-enum Serializable {
- Calc(CalcLengthPercentage),
- Length(Length),
- Percentage(Percentage),
-}
-
-impl LengthPercentage {
- /// 1px length value for SVG defaults
- #[inline]
- pub fn one() -> Self {
- Self::new_length(Length::new(1.))
- }
-
- /// 0%
- #[inline]
- pub fn zero_percent() -> Self {
- Self::new_percent(Percentage::zero())
- }
-
- fn to_calc_node(&self) -> Cow<CalcNode> {
- match self.unpack() {
- Unpacked::Length(l) => Cow::Owned(CalcNode::Leaf(CalcLengthPercentageLeaf::Length(l))),
- Unpacked::Percentage(p) => {
- Cow::Owned(CalcNode::Leaf(CalcLengthPercentageLeaf::Percentage(p)))
- },
- Unpacked::Calc(p) => Cow::Borrowed(&p.node),
- }
- }
-
- /// Constructs a length value.
- #[inline]
- pub fn new_length(length: Length) -> Self {
- let length = Self(LengthPercentageUnion {
- length: LengthVariant {
- tag: LengthPercentageUnion::TAG_LENGTH,
- length,
- },
- });
- debug_assert_eq!(length.tag(), Tag::Length);
- length
- }
-
- /// Constructs a percentage value.
- #[inline]
- pub fn new_percent(percentage: Percentage) -> Self {
- let percent = Self(LengthPercentageUnion {
- percentage: PercentageVariant {
- tag: LengthPercentageUnion::TAG_PERCENTAGE,
- percentage,
- },
- });
- debug_assert_eq!(percent.tag(), Tag::Percentage);
- percent
- }
-
- /// Given a `LengthPercentage` value `v`, construct the value representing
- /// `calc(100% - v)`.
- pub fn hundred_percent_minus(v: Self, clamping_mode: AllowedNumericType) -> Self {
- // TODO: This could in theory take ownership of the calc node in `v` if
- // possible instead of cloning.
- let mut node = v.to_calc_node().into_owned();
- node.map(std::ops::Neg::neg);
-
- let new_node = CalcNode::Sum(
- vec![
- CalcNode::Leaf(CalcLengthPercentageLeaf::Percentage(Percentage::hundred())),
- node,
- ]
- .into(),
- );
-
- Self::new_calc(new_node, clamping_mode)
- }
-
- /// Constructs a `calc()` value.
- #[inline]
- pub fn new_calc(mut node: CalcNode, clamping_mode: AllowedNumericType) -> Self {
- node.simplify_and_sort();
-
- match node {
- CalcNode::Leaf(l) => {
- return match l {
- CalcLengthPercentageLeaf::Length(l) => {
- Self::new_length(Length::new(clamping_mode.clamp(l.px())).normalized())
- },
- CalcLengthPercentageLeaf::Percentage(p) => Self::new_percent(Percentage(
- clamping_mode.clamp(crate::values::normalize(p.0)),
- )),
- }
- },
- _ => Self::new_calc_unchecked(Box::new(CalcLengthPercentage {
- clamping_mode,
- node,
- })),
- }
- }
-
- /// Private version of new_calc() that constructs a calc() variant without
- /// checking.
- fn new_calc_unchecked(calc: Box<CalcLengthPercentage>) -> Self {
- let ptr = Box::into_raw(calc);
-
- #[cfg(target_pointer_width = "32")]
- let calc = CalcVariant {
- tag: LengthPercentageUnion::TAG_CALC,
- ptr,
- };
-
- #[cfg(target_pointer_width = "64")]
- let calc = CalcVariant {
- #[cfg(target_endian = "little")]
- ptr: ptr as usize,
- #[cfg(target_endian = "big")]
- ptr: (ptr as usize).swap_bytes(),
- };
-
- let calc = Self(LengthPercentageUnion { calc });
- debug_assert_eq!(calc.tag(), Tag::Calc);
- calc
- }
-
- #[inline]
- fn tag(&self) -> Tag {
- match unsafe { self.0.tag.tag & LengthPercentageUnion::TAG_MASK } {
- LengthPercentageUnion::TAG_CALC => Tag::Calc,
- LengthPercentageUnion::TAG_LENGTH => Tag::Length,
- LengthPercentageUnion::TAG_PERCENTAGE => Tag::Percentage,
- _ => unsafe { debug_unreachable!("Bogus tag?") },
- }
- }
-
- #[inline]
- fn unpack_mut<'a>(&'a mut self) -> UnpackedMut<'a> {
- unsafe {
- match self.tag() {
- Tag::Calc => UnpackedMut::Calc(&mut *self.calc_ptr()),
- Tag::Length => UnpackedMut::Length(self.0.length.length),
- Tag::Percentage => UnpackedMut::Percentage(self.0.percentage.percentage),
- }
- }
- }
-
- #[inline]
- fn unpack<'a>(&'a self) -> Unpacked<'a> {
- unsafe {
- match self.tag() {
- Tag::Calc => Unpacked::Calc(&*self.calc_ptr()),
- Tag::Length => Unpacked::Length(self.0.length.length),
- Tag::Percentage => Unpacked::Percentage(self.0.percentage.percentage),
- }
- }
- }
-
- #[inline]
- unsafe fn calc_ptr(&self) -> *mut CalcLengthPercentage {
- #[cfg(not(all(target_endian = "big", target_pointer_width = "64")))]
- {
- self.0.calc.ptr as *mut _
- }
- #[cfg(all(target_endian = "big", target_pointer_width = "64"))]
- {
- self.0.calc.ptr.swap_bytes() as *mut _
- }
- }
-
- #[inline]
- fn to_serializable(&self) -> Serializable {
- match self.unpack() {
- Unpacked::Calc(c) => Serializable::Calc(c.clone()),
- Unpacked::Length(l) => Serializable::Length(l),
- Unpacked::Percentage(p) => Serializable::Percentage(p),
- }
- }
-
- #[inline]
- fn from_serializable(s: Serializable) -> Self {
- match s {
- Serializable::Calc(c) => Self::new_calc_unchecked(Box::new(c)),
- Serializable::Length(l) => Self::new_length(l),
- Serializable::Percentage(p) => Self::new_percent(p),
- }
- }
-
- /// Returns true if the computed value is absolute 0 or 0%.
- #[inline]
- pub fn is_definitely_zero(&self) -> bool {
- match self.unpack() {
- Unpacked::Length(l) => l.px() == 0.0,
- Unpacked::Percentage(p) => p.0 == 0.0,
- Unpacked::Calc(..) => false,
- }
- }
-
- /// Resolves the percentage.
- #[inline]
- pub fn resolve(&self, basis: Length) -> Length {
- match self.unpack() {
- Unpacked::Length(l) => l,
- Unpacked::Percentage(p) => (basis * p.0).normalized(),
- Unpacked::Calc(ref c) => c.resolve(basis),
- }
- }
-
- /// Resolves the percentage. Just an alias of resolve().
- #[inline]
- pub fn percentage_relative_to(&self, basis: Length) -> Length {
- self.resolve(basis)
- }
-
- /// Return whether there's any percentage in this value.
- #[inline]
- pub fn has_percentage(&self) -> bool {
- match self.unpack() {
- Unpacked::Length(..) => false,
- Unpacked::Percentage(..) | Unpacked::Calc(..) => true,
- }
- }
-
- /// Converts to a `<length>` if possible.
- pub fn to_length(&self) -> Option<Length> {
- match self.unpack() {
- Unpacked::Length(l) => Some(l),
- Unpacked::Percentage(..) | Unpacked::Calc(..) => {
- debug_assert!(self.has_percentage());
- return None;
- },
- }
- }
-
- /// Converts to a `<percentage>` if possible.
- #[inline]
- pub fn to_percentage(&self) -> Option<Percentage> {
- match self.unpack() {
- Unpacked::Percentage(p) => Some(p),
- Unpacked::Length(..) | Unpacked::Calc(..) => None,
- }
- }
-
- /// Returns the used value.
- #[inline]
- pub fn to_used_value(&self, containing_length: Au) -> Au {
- Au::from(self.to_pixel_length(containing_length))
- }
-
- /// Returns the used value as CSSPixelLength.
- #[inline]
- pub fn to_pixel_length(&self, containing_length: Au) -> Length {
- self.resolve(containing_length.into())
- }
-
- /// Convert the computed value into used value.
- #[inline]
- pub fn maybe_to_used_value(&self, container_len: Option<Length>) -> Option<Au> {
- self.maybe_percentage_relative_to(container_len)
- .map(Au::from)
- }
-
- /// If there are special rules for computing percentages in a value (e.g.
- /// the height property), they apply whenever a calc() expression contains
- /// percentages.
- pub fn maybe_percentage_relative_to(&self, container_len: Option<Length>) -> Option<Length> {
- if let Unpacked::Length(l) = self.unpack() {
- return Some(l);
- }
- Some(self.resolve(container_len?))
- }
-
- /// Returns the clamped non-negative values.
- #[inline]
- pub fn clamp_to_non_negative(mut self) -> Self {
- match self.unpack_mut() {
- UnpackedMut::Length(l) => Self::new_length(l.clamp_to_non_negative()),
- UnpackedMut::Percentage(p) => Self::new_percent(p.clamp_to_non_negative()),
- UnpackedMut::Calc(ref mut c) => {
- c.clamping_mode = AllowedNumericType::NonNegative;
- self
- },
- }
- }
-}
-
-impl PartialEq for LengthPercentage {
- fn eq(&self, other: &Self) -> bool {
- self.unpack() == other.unpack()
- }
-}
-
-impl fmt::Debug for LengthPercentage {
- fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
- self.unpack().fmt(formatter)
- }
-}
-
-impl ToAnimatedZero for LengthPercentage {
- fn to_animated_zero(&self) -> Result<Self, ()> {
- Ok(match self.unpack() {
- Unpacked::Length(l) => Self::new_length(l.to_animated_zero()?),
- Unpacked::Percentage(p) => Self::new_percent(p.to_animated_zero()?),
- Unpacked::Calc(c) => Self::new_calc_unchecked(Box::new(c.to_animated_zero()?)),
- })
- }
-}
-
-impl Clone for LengthPercentage {
- fn clone(&self) -> Self {
- match self.unpack() {
- Unpacked::Length(l) => Self::new_length(l),
- Unpacked::Percentage(p) => Self::new_percent(p),
- Unpacked::Calc(c) => Self::new_calc_unchecked(Box::new(c.clone())),
- }
- }
-}
-
-impl ToComputedValue for specified::LengthPercentage {
- type ComputedValue = LengthPercentage;
-
- fn to_computed_value(&self, context: &Context) -> LengthPercentage {
- match *self {
- specified::LengthPercentage::Length(ref value) => {
- LengthPercentage::new_length(value.to_computed_value(context))
- },
- specified::LengthPercentage::Percentage(value) => LengthPercentage::new_percent(value),
- specified::LengthPercentage::Calc(ref calc) => (**calc).to_computed_value(context),
- }
- }
-
- fn from_computed_value(computed: &LengthPercentage) -> Self {
- match computed.unpack() {
- Unpacked::Length(ref l) => {
- specified::LengthPercentage::Length(ToComputedValue::from_computed_value(l))
- },
- Unpacked::Percentage(p) => specified::LengthPercentage::Percentage(p),
- Unpacked::Calc(c) => {
- // We simplify before constructing the LengthPercentage if
- // needed, so this is always fine.
- specified::LengthPercentage::Calc(Box::new(
- specified::CalcLengthPercentage::from_computed_value(c),
- ))
- },
- }
- }
-}
-
-impl ComputeSquaredDistance for LengthPercentage {
- #[inline]
- fn compute_squared_distance(&self, other: &Self) -> Result<SquaredDistance, ()> {
- // A somewhat arbitrary base, it doesn't really make sense to mix
- // lengths with percentages, but we can't do much better here, and this
- // ensures that the distance between length-only and percentage-only
- // lengths makes sense.
- let basis = Length::new(100.);
- self.resolve(basis)
- .compute_squared_distance(&other.resolve(basis))
- }
-}
-
-impl ToCss for LengthPercentage {
- fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
- where
- W: Write,
- {
- self.unpack().to_css(dest)
- }
-}
-
-impl Zero for LengthPercentage {
- fn zero() -> Self {
- LengthPercentage::new_length(Length::zero())
- }
-
- #[inline]
- fn is_zero(&self) -> bool {
- self.is_definitely_zero()
- }
-}
-
-impl ZeroNoPercent for LengthPercentage {
- #[inline]
- fn is_zero_no_percent(&self) -> bool {
- self.is_definitely_zero() && !self.has_percentage()
- }
-}
-
-impl Serialize for LengthPercentage {
- fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
- where
- S: serde::Serializer,
- {
- self.to_serializable().serialize(serializer)
- }
-}
-
-impl<'de> Deserialize<'de> for LengthPercentage {
- fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
- where
- D: serde::Deserializer<'de>,
- {
- Ok(Self::from_serializable(Serializable::deserialize(
- deserializer,
- )?))
- }
-}
-
-/// The leaves of a `<length-percentage>` calc expression.
-#[derive(
- Clone,
- Debug,
- Deserialize,
- MallocSizeOf,
- PartialEq,
- Serialize,
- ToAnimatedZero,
- ToCss,
- ToResolvedValue,
-)]
-#[allow(missing_docs)]
-#[repr(u8)]
-pub enum CalcLengthPercentageLeaf {
- Length(Length),
- Percentage(Percentage),
-}
-
-impl CalcLengthPercentageLeaf {
- fn is_zero_length(&self) -> bool {
- match *self {
- Self::Length(ref l) => l.is_zero(),
- Self::Percentage(..) => false,
- }
- }
-}
-
-impl PartialOrd for CalcLengthPercentageLeaf {
- fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
- use self::CalcLengthPercentageLeaf::*;
- // NOTE: Percentages can't be compared reasonably here because the
- // percentage basis might be negative, see bug 1709018.
- match (self, other) {
- (&Length(ref one), &Length(ref other)) => one.partial_cmp(other),
- _ => None,
- }
- }
-}
-
-impl calc::CalcNodeLeaf for CalcLengthPercentageLeaf {
- fn unitless_value(&self) -> f32 {
- match *self {
- Self::Length(ref l) => l.px(),
- Self::Percentage(ref p) => p.0,
- }
- }
-
- fn try_sum_in_place(&mut self, other: &Self) -> Result<(), ()> {
- use self::CalcLengthPercentageLeaf::*;
-
- // 0px plus anything else is equal to the right hand side.
- if self.is_zero_length() {
- *self = other.clone();
- return Ok(());
- }
-
- if other.is_zero_length() {
- return Ok(());
- }
-
- match (self, other) {
- (&mut Length(ref mut one), &Length(ref other)) => {
- *one += *other;
- },
- (&mut Percentage(ref mut one), &Percentage(ref other)) => {
- one.0 += other.0;
- },
- _ => return Err(()),
- }
-
- Ok(())
- }
-
- fn try_op<O>(&self, other: &Self, op: O) -> Result<Self, ()>
- where
- O: Fn(f32, f32) -> f32,
- {
- match (self, other) {
- (
- &CalcLengthPercentageLeaf::Length(ref one),
- &CalcLengthPercentageLeaf::Length(ref other),
- ) => Ok(CalcLengthPercentageLeaf::Length(Length::new(op(
- one.px(),
- other.px(),
- )))),
- (
- &CalcLengthPercentageLeaf::Percentage(one),
- &CalcLengthPercentageLeaf::Percentage(other),
- ) => Ok(CalcLengthPercentageLeaf::Percentage(Percentage(op(
- one.0, other.0,
- )))),
- _ => Err(()),
- }
- }
-
- fn map(&mut self, mut op: impl FnMut(f32) -> f32) {
- match self {
- CalcLengthPercentageLeaf::Length(value) => {
- *value = Length::new(op(value.px()));
- },
- CalcLengthPercentageLeaf::Percentage(value) => {
- *value = Percentage(op(value.0));
- },
- }
- }
-
- fn simplify(&mut self) {}
-
- fn sort_key(&self) -> calc::SortKey {
- match *self {
- Self::Length(..) => calc::SortKey::Px,
- Self::Percentage(..) => calc::SortKey::Percentage,
- }
- }
-}
-
-/// The computed version of a calc() node for `<length-percentage>` values.
-pub type CalcNode = calc::GenericCalcNode<CalcLengthPercentageLeaf>;
-
-/// The representation of a calc() function with mixed lengths and percentages.
-#[derive(
- Clone, Debug, Deserialize, MallocSizeOf, Serialize, ToAnimatedZero, ToResolvedValue, ToCss,
-)]
-#[repr(C)]
-pub struct CalcLengthPercentage {
- #[animation(constant)]
- #[css(skip)]
- clamping_mode: AllowedNumericType,
- node: CalcNode,
-}
-
-impl CalcLengthPercentage {
- /// Resolves the percentage.
- #[inline]
- fn resolve(&self, basis: Length) -> Length {
- // unwrap() is fine because the conversion below is infallible.
- let px = self
- .node
- .resolve(|l| {
- Ok(match *l {
- CalcLengthPercentageLeaf::Length(l) => l.px(),
- CalcLengthPercentageLeaf::Percentage(ref p) => basis.px() * p.0,
- })
- })
- .unwrap();
- Length::new(self.clamping_mode.clamp(px)).normalized()
- }
-}
-
-// NOTE(emilio): We don't compare `clamping_mode` since we want to preserve the
-// invariant that `from_computed_value(length).to_computed_value(..) == length`.
-//
-// Right now for e.g. a non-negative length, we set clamping_mode to `All`
-// unconditionally for non-calc values, and to `NonNegative` for calc.
-//
-// If we determine that it's sound, from_computed_value() can generate an
-// absolute length, which then would get `All` as the clamping mode.
-//
-// We may want to just eagerly-detect whether we can clamp in
-// `LengthPercentage::new` and switch to `AllowedNumericType::NonNegative` then,
-// maybe.
-impl PartialEq for CalcLengthPercentage {
- fn eq(&self, other: &Self) -> bool {
- self.node == other.node
- }
-}
-
-impl specified::CalcLengthPercentage {
- /// Compute the value, zooming any absolute units by the zoom function.
- fn to_computed_value_with_zoom<F>(
- &self,
- context: &Context,
- zoom_fn: F,
- base_size: FontBaseSize,
- ) -> LengthPercentage
- where
- F: Fn(Length) -> Length,
- {
- use crate::values::specified::calc::Leaf;
-
- let node = self.node.map_leaves(|leaf| match *leaf {
- Leaf::Percentage(p) => CalcLengthPercentageLeaf::Percentage(Percentage(p)),
- Leaf::Length(l) => CalcLengthPercentageLeaf::Length({
- let result = l.to_computed_value_with_base_size(context, base_size);
- if l.should_zoom_text() {
- zoom_fn(result)
- } else {
- result
- }
- }),
- Leaf::Number(..) | Leaf::Angle(..) | Leaf::Time(..) | Leaf::Resolution(..) => {
- unreachable!("Shouldn't have parsed")
- },
- });
-
- LengthPercentage::new_calc(node, self.clamping_mode)
- }
-
- /// Compute font-size or line-height taking into account text-zoom if necessary.
- pub fn to_computed_value_zoomed(
- &self,
- context: &Context,
- base_size: FontBaseSize,
- ) -> LengthPercentage {
- self.to_computed_value_with_zoom(context, |abs| context.maybe_zoom_text(abs), base_size)
- }
-
- /// Compute the value into pixel length as CSSFloat without context,
- /// so it returns Err(()) if there is any non-absolute unit.
- pub fn to_computed_pixel_length_without_context(&self) -> Result<CSSFloat, ()> {
- use crate::values::specified::calc::Leaf;
- use crate::values::specified::length::NoCalcLength;
-
- // Simplification should've turned this into an absolute length,
- // otherwise it wouldn't have been able to.
- match self.node {
- calc::CalcNode::Leaf(Leaf::Length(NoCalcLength::Absolute(ref l))) => Ok(l.to_px()),
- _ => Err(()),
- }
- }
-
- /// Compute the calc using the current font-size (and without text-zoom).
- pub fn to_computed_value(&self, context: &Context) -> LengthPercentage {
- self.to_computed_value_with_zoom(context, |abs| abs, FontBaseSize::CurrentStyle)
- }
-
- #[inline]
- fn from_computed_value(computed: &CalcLengthPercentage) -> Self {
- use crate::values::specified::calc::Leaf;
- use crate::values::specified::length::NoCalcLength;
-
- specified::CalcLengthPercentage {
- clamping_mode: computed.clamping_mode,
- node: computed.node.map_leaves(|l| match l {
- CalcLengthPercentageLeaf::Length(ref l) => {
- Leaf::Length(NoCalcLength::from_px(l.px()))
- },
- CalcLengthPercentageLeaf::Percentage(ref p) => Leaf::Percentage(p.0),
- }),
- }
- }
-}
-
-/// https://drafts.csswg.org/css-transitions/#animtype-lpcalc
-/// https://drafts.csswg.org/css-values-4/#combine-math
-/// https://drafts.csswg.org/css-values-4/#combine-mixed
-impl Animate for LengthPercentage {
- #[inline]
- fn animate(&self, other: &Self, procedure: Procedure) -> Result<Self, ()> {
- Ok(match (self.unpack(), other.unpack()) {
- (Unpacked::Length(one), Unpacked::Length(other)) => {
- Self::new_length(one.animate(&other, procedure)?)
- },
- (Unpacked::Percentage(one), Unpacked::Percentage(other)) => {
- Self::new_percent(one.animate(&other, procedure)?)
- },
- _ => {
- let mut one = self.to_calc_node().into_owned();
- let mut other = other.to_calc_node().into_owned();
- let (l, r) = procedure.weights();
-
- one.mul_by(l as f32);
- other.mul_by(r as f32);
-
- Self::new_calc(
- CalcNode::Sum(vec![one, other].into()),
- AllowedNumericType::All,
- )
- },
- })
- }
-}
-
-/// A wrapper of LengthPercentage, whose value must be >= 0.
-pub type NonNegativeLengthPercentage = NonNegative<LengthPercentage>;
-
-impl ToAnimatedValue for NonNegativeLengthPercentage {
- type AnimatedValue = LengthPercentage;
-
- #[inline]
- fn to_animated_value(self) -> Self::AnimatedValue {
- self.0
- }
-
- #[inline]
- fn from_animated_value(animated: Self::AnimatedValue) -> Self {
- NonNegative(animated.clamp_to_non_negative())
- }
-}
-
-impl NonNegativeLengthPercentage {
- /// Returns true if the computed value is absolute 0 or 0%.
- #[inline]
- pub fn is_definitely_zero(&self) -> bool {
- self.0.is_definitely_zero()
- }
-
- /// Returns the used value.
- #[inline]
- pub fn to_used_value(&self, containing_length: Au) -> Au {
- let resolved = self.0.to_used_value(containing_length);
- std::cmp::max(resolved, Au(0))
- }
-
- /// Convert the computed value into used value.
- #[inline]
- pub fn maybe_to_used_value(&self, containing_length: Option<Au>) -> Option<Au> {
- let resolved = self
- .0
- .maybe_to_used_value(containing_length.map(|v| v.into()))?;
- Some(std::cmp::max(resolved, Au(0)))
- }
-}
diff --git a/components/style/values/computed/list.rs b/components/style/values/computed/list.rs
deleted file mode 100644
index 3e5d1eb220d..00000000000
--- a/components/style/values/computed/list.rs
+++ /dev/null
@@ -1,17 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
-
-//! `list` computed values.
-
-#[cfg(feature = "gecko")]
-pub use crate::values::specified::list::ListStyleType;
-pub use crate::values::specified::list::Quotes;
-
-impl Quotes {
- /// Initial value for `quotes`.
- #[inline]
- pub fn get_initial_value() -> Quotes {
- Quotes::Auto
- }
-}
diff --git a/components/style/values/computed/mod.rs b/components/style/values/computed/mod.rs
deleted file mode 100644
index 8f9af36c478..00000000000
--- a/components/style/values/computed/mod.rs
+++ /dev/null
@@ -1,998 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
-
-//! Computed values.
-
-use self::transform::DirectionVector;
-use super::animated::ToAnimatedValue;
-use super::generics::grid::GridTemplateComponent as GenericGridTemplateComponent;
-use super::generics::grid::ImplicitGridTracks as GenericImplicitGridTracks;
-use super::generics::grid::{GenericGridLine, GenericTrackBreadth};
-use super::generics::grid::{GenericTrackSize, TrackList as GenericTrackList};
-use super::generics::transform::IsParallelTo;
-use super::generics::{self, GreaterThanOrEqualToOne, NonNegative, ZeroToOne};
-use super::specified;
-use super::{CSSFloat, CSSInteger};
-use crate::computed_value_flags::ComputedValueFlags;
-use crate::context::QuirksMode;
-use crate::font_metrics::{FontMetrics, FontMetricsOrientation};
-use crate::media_queries::Device;
-#[cfg(feature = "gecko")]
-use crate::properties;
-use crate::properties::{ComputedValues, StyleBuilder};
-use crate::rule_cache::RuleCacheConditions;
-use crate::stylesheets::container_rule::{
- ContainerInfo, ContainerSizeQuery, ContainerSizeQueryResult,
-};
-use crate::values::specified::length::FontBaseSize;
-use crate::{ArcSlice, Atom, One};
-use euclid::{default, Point2D, Rect, Size2D};
-use servo_arc::Arc;
-use std::cell::RefCell;
-use std::cmp;
-use std::f32;
-use std::ops::{Add, Sub};
-
-#[cfg(feature = "gecko")]
-pub use self::align::{
- AlignContent, AlignItems, AlignTracks, JustifyContent, JustifyItems, JustifyTracks,
- SelfAlignment,
-};
-#[cfg(feature = "gecko")]
-pub use self::align::{AlignSelf, JustifySelf};
-pub use self::angle::Angle;
-pub use self::animation::{AnimationIterationCount, AnimationName, AnimationTimeline};
-pub use self::animation::{ScrollAxis, ScrollTimelineName, TransitionProperty, ViewTimelineInset};
-pub use self::background::{BackgroundRepeat, BackgroundSize};
-pub use self::basic_shape::FillRule;
-pub use self::border::{
- BorderCornerRadius, BorderImageRepeat, BorderImageSideWidth, BorderImageSlice,
- BorderImageWidth, BorderRadius, BorderSideWidth, BorderSpacing, LineWidth,
-};
-pub use self::box_::{
- Appearance, BaselineSource, BreakBetween, BreakWithin, Clear, Contain, ContainIntrinsicSize,
- ContainerName, ContainerType, ContentVisibility, Display, Float, LineClamp, Overflow,
- OverflowAnchor, OverflowClipBox, OverscrollBehavior, Perspective, Resize, ScrollSnapAlign,
- ScrollSnapAxis, ScrollSnapStop, ScrollSnapStrictness, ScrollSnapType, ScrollbarGutter,
- TouchAction, VerticalAlign, WillChange,
-};
-pub use self::color::{
- Color, ColorOrAuto, ColorPropertyValue, ColorScheme, ForcedColorAdjust, PrintColorAdjust,
-};
-pub use self::column::ColumnCount;
-pub use self::counters::{Content, ContentItem, CounterIncrement, CounterReset, CounterSet};
-pub use self::easing::TimingFunction;
-pub use self::effects::{BoxShadow, Filter, SimpleShadow};
-pub use self::flex::FlexBasis;
-pub use self::font::{FontFamily, FontLanguageOverride, FontPalette, FontStyle};
-pub use self::font::{FontFeatureSettings, FontVariantLigatures, FontVariantNumeric};
-pub use self::font::{FontSize, FontSizeAdjust, FontStretch, FontSynthesis};
-pub use self::font::{FontVariantAlternates, FontWeight};
-pub use self::font::{FontVariantEastAsian, FontVariationSettings};
-pub use self::font::{MathDepth, MozScriptMinSize, MozScriptSizeMultiplier, XLang, XTextScale};
-pub use self::image::{Gradient, Image, ImageRendering, LineDirection, MozImageRect};
-pub use self::length::{CSSPixelLength, NonNegativeLength};
-pub use self::length::{Length, LengthOrNumber, LengthPercentage, NonNegativeLengthOrNumber};
-pub use self::length::{LengthOrAuto, LengthPercentageOrAuto, MaxSize, Size};
-pub use self::length::{NonNegativeLengthPercentage, NonNegativeLengthPercentageOrAuto};
-#[cfg(feature = "gecko")]
-pub use self::list::ListStyleType;
-pub use self::list::Quotes;
-pub use self::motion::{OffsetPath, OffsetPosition, OffsetRotate};
-pub use self::outline::OutlineStyle;
-pub use self::page::{PageName, PageOrientation, PageSize, PageSizeOrientation, PaperSize};
-pub use self::percentage::{NonNegativePercentage, Percentage};
-pub use self::position::AspectRatio;
-pub use self::position::{
- GridAutoFlow, GridTemplateAreas, MasonryAutoFlow, Position, PositionOrAuto, ZIndex,
-};
-pub use self::ratio::Ratio;
-pub use self::rect::NonNegativeLengthOrNumberRect;
-pub use self::resolution::Resolution;
-pub use self::svg::{DProperty, MozContextProperties};
-pub use self::svg::{SVGLength, SVGOpacity, SVGPaint, SVGPaintKind};
-pub use self::svg::{SVGPaintOrder, SVGStrokeDashArray, SVGWidth};
-pub use self::text::HyphenateCharacter;
-pub use self::text::TextUnderlinePosition;
-pub use self::text::{InitialLetter, LetterSpacing, LineBreak, LineHeight};
-pub use self::text::{OverflowWrap, RubyPosition, TextOverflow, WordBreak, WordSpacing};
-pub use self::text::{TextAlign, TextAlignLast, TextEmphasisPosition, TextEmphasisStyle};
-pub use self::text::{TextDecorationLength, TextDecorationSkipInk, TextJustify};
-pub use self::time::Time;
-pub use self::transform::{Rotate, Scale, Transform, TransformOperation};
-pub use self::transform::{TransformOrigin, TransformStyle, Translate};
-#[cfg(feature = "gecko")]
-pub use self::ui::CursorImage;
-pub use self::ui::{BoolInteger, Cursor, UserSelect};
-pub use super::specified::TextTransform;
-pub use super::specified::ViewportVariant;
-pub use super::specified::{BorderStyle, TextDecorationLine};
-pub use app_units::Au;
-
-#[cfg(feature = "gecko")]
-pub mod align;
-pub mod angle;
-pub mod animation;
-pub mod background;
-pub mod basic_shape;
-pub mod border;
-#[path = "box.rs"]
-pub mod box_;
-pub mod color;
-pub mod column;
-pub mod counters;
-pub mod easing;
-pub mod effects;
-pub mod flex;
-pub mod font;
-pub mod image;
-pub mod length;
-pub mod length_percentage;
-pub mod list;
-pub mod motion;
-pub mod outline;
-pub mod page;
-pub mod percentage;
-pub mod position;
-pub mod ratio;
-pub mod rect;
-pub mod resolution;
-pub mod svg;
-pub mod table;
-pub mod text;
-pub mod time;
-pub mod transform;
-pub mod ui;
-pub mod url;
-
-/// A `Context` is all the data a specified value could ever need to compute
-/// itself and be transformed to a computed value.
-pub struct Context<'a> {
- /// Values accessed through this need to be in the properties "computed
- /// early": color, text-decoration, font-size, display, position, float,
- /// border-*-style, outline-style, font-family, writing-mode...
- pub builder: StyleBuilder<'a>,
-
- /// A cached computed system font value, for use by gecko.
- ///
- /// See properties/longhands/font.mako.rs
- #[cfg(feature = "gecko")]
- pub cached_system_font: Option<properties::longhands::system_font::ComputedSystemFont>,
-
- /// A dummy option for servo so initializing a computed::Context isn't
- /// painful.
- ///
- /// TODO(emilio): Make constructors for Context, and drop this.
- #[cfg(feature = "servo")]
- pub cached_system_font: Option<()>,
-
- /// Whether or not we are computing the media list in a media query.
- pub in_media_query: bool,
-
- /// Whether or not we are computing the container query condition.
- pub in_container_query: bool,
-
- /// The quirks mode of this context.
- pub quirks_mode: QuirksMode,
-
- /// Whether this computation is being done for a SMIL animation.
- ///
- /// This is used to allow certain properties to generate out-of-range
- /// values, which SMIL allows.
- pub for_smil_animation: bool,
-
- /// Returns the container information to evaluate a given container query.
- pub container_info: Option<ContainerInfo>,
-
- /// Whether we're computing a value for a non-inherited property.
- /// False if we are computed a value for an inherited property or not computing for a property
- /// at all (e.g. in a media query evaluation).
- pub for_non_inherited_property: bool,
-
- /// The conditions to cache a rule node on the rule cache.
- ///
- /// FIXME(emilio): Drop the refcell.
- pub rule_cache_conditions: RefCell<&'a mut RuleCacheConditions>,
-
- /// Container size query for this context.
- container_size_query: RefCell<ContainerSizeQuery<'a>>,
-}
-
-impl<'a> Context<'a> {
- /// Lazily evaluate the container size query, returning the result.
- pub fn get_container_size_query(&self) -> ContainerSizeQueryResult {
- let mut resolved = self.container_size_query.borrow_mut();
- resolved.get().clone()
- }
-
- /// Creates a suitable context for media query evaluation, in which
- /// font-relative units compute against the system_font, and executes `f`
- /// with it.
- pub fn for_media_query_evaluation<F, R>(device: &Device, quirks_mode: QuirksMode, f: F) -> R
- where
- F: FnOnce(&Context) -> R,
- {
- let mut conditions = RuleCacheConditions::default();
- let context = Context {
- builder: StyleBuilder::for_inheritance(device, None, None),
- cached_system_font: None,
- in_media_query: true,
- in_container_query: false,
- quirks_mode,
- for_smil_animation: false,
- container_info: None,
- for_non_inherited_property: false,
- rule_cache_conditions: RefCell::new(&mut conditions),
- container_size_query: RefCell::new(ContainerSizeQuery::none()),
- };
- f(&context)
- }
-
- /// Creates a suitable context for container query evaluation for the style
- /// specified.
- pub fn for_container_query_evaluation<F, R>(
- device: &Device,
- container_info_and_style: Option<(ContainerInfo, Arc<ComputedValues>)>,
- container_size_query: ContainerSizeQuery,
- f: F,
- ) -> R
- where
- F: FnOnce(&Context) -> R,
- {
- let mut conditions = RuleCacheConditions::default();
-
- let (container_info, style) = match container_info_and_style {
- Some((ci, s)) => (Some(ci), Some(s)),
- None => (None, None),
- };
-
- let style = style.as_ref().map(|s| &**s);
- let quirks_mode = device.quirks_mode();
- let context = Context {
- builder: StyleBuilder::for_inheritance(device, style, None),
- cached_system_font: None,
- in_media_query: false,
- in_container_query: true,
- quirks_mode,
- for_smil_animation: false,
- container_info,
- for_non_inherited_property: false,
- rule_cache_conditions: RefCell::new(&mut conditions),
- container_size_query: RefCell::new(container_size_query),
- };
-
- f(&context)
- }
-
- /// Creates a context suitable for more general cases.
- pub fn new(
- builder: StyleBuilder<'a>,
- quirks_mode: QuirksMode,
- rule_cache_conditions: &'a mut RuleCacheConditions,
- container_size_query: ContainerSizeQuery<'a>,
- ) -> Self {
- Self {
- builder,
- cached_system_font: None,
- in_media_query: false,
- in_container_query: false,
- quirks_mode,
- container_info: None,
- for_smil_animation: false,
- for_non_inherited_property: false,
- rule_cache_conditions: RefCell::new(rule_cache_conditions),
- container_size_query: RefCell::new(container_size_query),
- }
- }
-
- /// Creates a context suitable for computing animations.
- pub fn new_for_animation(
- builder: StyleBuilder<'a>,
- for_smil_animation: bool,
- quirks_mode: QuirksMode,
- rule_cache_conditions: &'a mut RuleCacheConditions,
- container_size_query: ContainerSizeQuery<'a>,
- ) -> Self {
- Self {
- builder,
- cached_system_font: None,
- in_media_query: false,
- in_container_query: false,
- quirks_mode,
- container_info: None,
- for_smil_animation,
- for_non_inherited_property: false,
- rule_cache_conditions: RefCell::new(rule_cache_conditions),
- container_size_query: RefCell::new(container_size_query),
- }
- }
-
- /// The current device.
- pub fn device(&self) -> &Device {
- self.builder.device
- }
-
- /// Queries font metrics.
- pub fn query_font_metrics(
- &self,
- base_size: FontBaseSize,
- orientation: FontMetricsOrientation,
- retrieve_math_scales: bool,
- ) -> FontMetrics {
- if self.for_non_inherited_property {
- self.rule_cache_conditions.borrow_mut().set_uncacheable();
- }
- self.builder.add_flags(match base_size {
- FontBaseSize::CurrentStyle => ComputedValueFlags::DEPENDS_ON_SELF_FONT_METRICS,
- FontBaseSize::InheritedStyle => ComputedValueFlags::DEPENDS_ON_INHERITED_FONT_METRICS,
- });
- let size = base_size.resolve(self).used_size();
- let style = self.style();
-
- let (wm, font) = match base_size {
- FontBaseSize::CurrentStyle => (style.writing_mode, style.get_font()),
- // This is only used for font-size computation.
- FontBaseSize::InheritedStyle => {
- (*style.inherited_writing_mode(), style.get_parent_font())
- },
- };
-
- let vertical = match orientation {
- FontMetricsOrientation::MatchContextPreferHorizontal => {
- wm.is_vertical() && wm.is_upright()
- },
- FontMetricsOrientation::MatchContextPreferVertical => {
- wm.is_vertical() && !wm.is_sideways()
- },
- FontMetricsOrientation::Horizontal => false,
- };
- self.device().query_font_metrics(
- vertical,
- font,
- size,
- self.in_media_or_container_query(),
- retrieve_math_scales,
- )
- }
-
- /// The current viewport size, used to resolve viewport units.
- pub fn viewport_size_for_viewport_unit_resolution(
- &self,
- variant: ViewportVariant,
- ) -> default::Size2D<Au> {
- self.builder
- .add_flags(ComputedValueFlags::USES_VIEWPORT_UNITS);
- self.builder
- .device
- .au_viewport_size_for_viewport_unit_resolution(variant)
- }
-
- /// Whether we're in a media or container query.
- pub fn in_media_or_container_query(&self) -> bool {
- self.in_media_query || self.in_container_query
- }
-
- /// The default computed style we're getting our reset style from.
- pub fn default_style(&self) -> &ComputedValues {
- self.builder.default_style()
- }
-
- /// The current style.
- pub fn style(&self) -> &StyleBuilder {
- &self.builder
- }
-
- /// Apply text-zoom if enabled.
- #[cfg(feature = "gecko")]
- pub fn maybe_zoom_text(&self, size: CSSPixelLength) -> CSSPixelLength {
- if self
- .style()
- .get_font()
- .clone__x_text_scale()
- .text_zoom_enabled()
- {
- self.device().zoom_text(size)
- } else {
- size
- }
- }
-
- /// (Servo doesn't do text-zoom)
- #[cfg(feature = "servo")]
- pub fn maybe_zoom_text(&self, size: CSSPixelLength) -> CSSPixelLength {
- size
- }
-}
-
-/// An iterator over a slice of computed values
-#[derive(Clone)]
-pub struct ComputedVecIter<'a, 'cx, 'cx_a: 'cx, S: ToComputedValue + 'a> {
- cx: &'cx Context<'cx_a>,
- values: &'a [S],
-}
-
-impl<'a, 'cx, 'cx_a: 'cx, S: ToComputedValue + 'a> ComputedVecIter<'a, 'cx, 'cx_a, S> {
- /// Construct an iterator from a slice of specified values and a context
- pub fn new(cx: &'cx Context<'cx_a>, values: &'a [S]) -> Self {
- ComputedVecIter { cx, values }
- }
-}
-
-impl<'a, 'cx, 'cx_a: 'cx, S: ToComputedValue + 'a> ExactSizeIterator
- for ComputedVecIter<'a, 'cx, 'cx_a, S>
-{
- fn len(&self) -> usize {
- self.values.len()
- }
-}
-
-impl<'a, 'cx, 'cx_a: 'cx, S: ToComputedValue + 'a> Iterator for ComputedVecIter<'a, 'cx, 'cx_a, S> {
- type Item = S::ComputedValue;
- fn next(&mut self) -> Option<Self::Item> {
- if let Some((next, rest)) = self.values.split_first() {
- let ret = next.to_computed_value(self.cx);
- self.values = rest;
- Some(ret)
- } else {
- None
- }
- }
-
- fn size_hint(&self) -> (usize, Option<usize>) {
- (self.values.len(), Some(self.values.len()))
- }
-}
-
-/// A trait to represent the conversion between computed and specified values.
-///
-/// This trait is derivable with `#[derive(ToComputedValue)]`. The derived
-/// implementation just calls `ToComputedValue::to_computed_value` on each field
-/// of the passed value. The deriving code assumes that if the type isn't
-/// generic, then the trait can be implemented as simple `Clone::clone` calls,
-/// this means that a manual implementation with `ComputedValue = Self` is bogus
-/// if it returns anything else than a clone.
-pub trait ToComputedValue {
- /// The computed value type we're going to be converted to.
- type ComputedValue;
-
- /// Convert a specified value to a computed value, using itself and the data
- /// inside the `Context`.
- fn to_computed_value(&self, context: &Context) -> Self::ComputedValue;
-
- /// Convert a computed value to specified value form.
- ///
- /// This will be used for recascading during animation.
- /// Such from_computed_valued values should recompute to the same value.
- fn from_computed_value(computed: &Self::ComputedValue) -> Self;
-}
-
-impl<A, B> ToComputedValue for (A, B)
-where
- A: ToComputedValue,
- B: ToComputedValue,
-{
- type ComputedValue = (
- <A as ToComputedValue>::ComputedValue,
- <B as ToComputedValue>::ComputedValue,
- );
-
- #[inline]
- fn to_computed_value(&self, context: &Context) -> Self::ComputedValue {
- (
- self.0.to_computed_value(context),
- self.1.to_computed_value(context),
- )
- }
-
- #[inline]
- fn from_computed_value(computed: &Self::ComputedValue) -> Self {
- (
- A::from_computed_value(&computed.0),
- B::from_computed_value(&computed.1),
- )
- }
-}
-
-impl<T> ToComputedValue for Option<T>
-where
- T: ToComputedValue,
-{
- type ComputedValue = Option<<T as ToComputedValue>::ComputedValue>;
-
- #[inline]
- fn to_computed_value(&self, context: &Context) -> Self::ComputedValue {
- self.as_ref().map(|item| item.to_computed_value(context))
- }
-
- #[inline]
- fn from_computed_value(computed: &Self::ComputedValue) -> Self {
- computed.as_ref().map(T::from_computed_value)
- }
-}
-
-impl<T> ToComputedValue for default::Size2D<T>
-where
- T: ToComputedValue,
-{
- type ComputedValue = default::Size2D<<T as ToComputedValue>::ComputedValue>;
-
- #[inline]
- fn to_computed_value(&self, context: &Context) -> Self::ComputedValue {
- Size2D::new(
- self.width.to_computed_value(context),
- self.height.to_computed_value(context),
- )
- }
-
- #[inline]
- fn from_computed_value(computed: &Self::ComputedValue) -> Self {
- Size2D::new(
- T::from_computed_value(&computed.width),
- T::from_computed_value(&computed.height),
- )
- }
-}
-
-impl<T> ToComputedValue for Vec<T>
-where
- T: ToComputedValue,
-{
- type ComputedValue = Vec<<T as ToComputedValue>::ComputedValue>;
-
- #[inline]
- fn to_computed_value(&self, context: &Context) -> Self::ComputedValue {
- self.iter()
- .map(|item| item.to_computed_value(context))
- .collect()
- }
-
- #[inline]
- fn from_computed_value(computed: &Self::ComputedValue) -> Self {
- computed.iter().map(T::from_computed_value).collect()
- }
-}
-
-impl<T> ToComputedValue for Box<T>
-where
- T: ToComputedValue,
-{
- type ComputedValue = Box<<T as ToComputedValue>::ComputedValue>;
-
- #[inline]
- fn to_computed_value(&self, context: &Context) -> Self::ComputedValue {
- Box::new(T::to_computed_value(self, context))
- }
-
- #[inline]
- fn from_computed_value(computed: &Self::ComputedValue) -> Self {
- Box::new(T::from_computed_value(computed))
- }
-}
-
-impl<T> ToComputedValue for Box<[T]>
-where
- T: ToComputedValue,
-{
- type ComputedValue = Box<[<T as ToComputedValue>::ComputedValue]>;
-
- #[inline]
- fn to_computed_value(&self, context: &Context) -> Self::ComputedValue {
- self.iter()
- .map(|item| item.to_computed_value(context))
- .collect::<Vec<_>>()
- .into_boxed_slice()
- }
-
- #[inline]
- fn from_computed_value(computed: &Self::ComputedValue) -> Self {
- computed
- .iter()
- .map(T::from_computed_value)
- .collect::<Vec<_>>()
- .into_boxed_slice()
- }
-}
-
-impl<T> ToComputedValue for crate::OwnedSlice<T>
-where
- T: ToComputedValue,
-{
- type ComputedValue = crate::OwnedSlice<<T as ToComputedValue>::ComputedValue>;
-
- #[inline]
- fn to_computed_value(&self, context: &Context) -> Self::ComputedValue {
- self.iter()
- .map(|item| item.to_computed_value(context))
- .collect()
- }
-
- #[inline]
- fn from_computed_value(computed: &Self::ComputedValue) -> Self {
- computed.iter().map(T::from_computed_value).collect()
- }
-}
-
-// NOTE(emilio): This is implementable more generically, but it's unlikely
-// what you want there, as it forces you to have an extra allocation.
-//
-// We could do that if needed, ideally with specialization for the case where
-// ComputedValue = T. But we don't need it for now.
-impl<T> ToComputedValue for Arc<T>
-where
- T: ToComputedValue<ComputedValue = T>,
-{
- type ComputedValue = Self;
-
- #[inline]
- fn to_computed_value(&self, _: &Context) -> Self {
- self.clone()
- }
-
- #[inline]
- fn from_computed_value(computed: &Self) -> Self {
- computed.clone()
- }
-}
-
-// Same caveat as above applies.
-impl<T> ToComputedValue for ArcSlice<T>
-where
- T: ToComputedValue<ComputedValue = T>,
-{
- type ComputedValue = Self;
-
- #[inline]
- fn to_computed_value(&self, _: &Context) -> Self {
- self.clone()
- }
-
- #[inline]
- fn from_computed_value(computed: &Self) -> Self {
- computed.clone()
- }
-}
-
-trivial_to_computed_value!(());
-trivial_to_computed_value!(bool);
-trivial_to_computed_value!(f32);
-trivial_to_computed_value!(i32);
-trivial_to_computed_value!(u8);
-trivial_to_computed_value!(u16);
-trivial_to_computed_value!(u32);
-trivial_to_computed_value!(usize);
-trivial_to_computed_value!(Atom);
-trivial_to_computed_value!(crate::values::AtomIdent);
-#[cfg(feature = "servo")]
-trivial_to_computed_value!(crate::Namespace);
-#[cfg(feature = "servo")]
-trivial_to_computed_value!(crate::Prefix);
-trivial_to_computed_value!(String);
-trivial_to_computed_value!(Box<str>);
-trivial_to_computed_value!(crate::OwnedStr);
-trivial_to_computed_value!(style_traits::values::specified::AllowedNumericType);
-
-#[allow(missing_docs)]
-#[derive(
- Animate,
- Clone,
- ComputeSquaredDistance,
- Copy,
- Debug,
- MallocSizeOf,
- PartialEq,
- ToAnimatedZero,
- ToCss,
- ToResolvedValue,
-)]
-#[repr(C, u8)]
-pub enum AngleOrPercentage {
- Percentage(Percentage),
- Angle(Angle),
-}
-
-impl ToComputedValue for specified::AngleOrPercentage {
- type ComputedValue = AngleOrPercentage;
-
- #[inline]
- fn to_computed_value(&self, context: &Context) -> AngleOrPercentage {
- match *self {
- specified::AngleOrPercentage::Percentage(percentage) => {
- AngleOrPercentage::Percentage(percentage.to_computed_value(context))
- },
- specified::AngleOrPercentage::Angle(angle) => {
- AngleOrPercentage::Angle(angle.to_computed_value(context))
- },
- }
- }
- #[inline]
- fn from_computed_value(computed: &AngleOrPercentage) -> Self {
- match *computed {
- AngleOrPercentage::Percentage(percentage) => specified::AngleOrPercentage::Percentage(
- ToComputedValue::from_computed_value(&percentage),
- ),
- AngleOrPercentage::Angle(angle) => {
- specified::AngleOrPercentage::Angle(ToComputedValue::from_computed_value(&angle))
- },
- }
- }
-}
-
-/// A `<number>` value.
-pub type Number = CSSFloat;
-
-impl IsParallelTo for (Number, Number, Number) {
- fn is_parallel_to(&self, vector: &DirectionVector) -> bool {
- use euclid::approxeq::ApproxEq;
- // If a and b is parallel, the angle between them is 0deg, so
- // a x b = |a|*|b|*sin(0)*n = 0 * n, |a x b| == 0.
- let self_vector = DirectionVector::new(self.0, self.1, self.2);
- self_vector
- .cross(*vector)
- .square_length()
- .approx_eq(&0.0f32)
- }
-}
-
-/// A wrapper of Number, but the value >= 0.
-pub type NonNegativeNumber = NonNegative<CSSFloat>;
-
-impl ToAnimatedValue for NonNegativeNumber {
- type AnimatedValue = CSSFloat;
-
- #[inline]
- fn to_animated_value(self) -> Self::AnimatedValue {
- self.0
- }
-
- #[inline]
- fn from_animated_value(animated: Self::AnimatedValue) -> Self {
- animated.max(0.).into()
- }
-}
-
-impl From<CSSFloat> for NonNegativeNumber {
- #[inline]
- fn from(number: CSSFloat) -> NonNegativeNumber {
- NonNegative::<CSSFloat>(number)
- }
-}
-
-impl From<NonNegativeNumber> for CSSFloat {
- #[inline]
- fn from(number: NonNegativeNumber) -> CSSFloat {
- number.0
- }
-}
-
-impl One for NonNegativeNumber {
- #[inline]
- fn one() -> Self {
- NonNegative(1.0)
- }
-
- #[inline]
- fn is_one(&self) -> bool {
- self.0 == 1.0
- }
-}
-
-/// A wrapper of Number, but the value between 0 and 1
-pub type ZeroToOneNumber = ZeroToOne<CSSFloat>;
-
-impl ToAnimatedValue for ZeroToOneNumber {
- type AnimatedValue = CSSFloat;
-
- #[inline]
- fn to_animated_value(self) -> Self::AnimatedValue {
- self.0
- }
-
- #[inline]
- fn from_animated_value(animated: Self::AnimatedValue) -> Self {
- Self(animated.max(0.).min(1.))
- }
-}
-
-impl From<CSSFloat> for ZeroToOneNumber {
- #[inline]
- fn from(number: CSSFloat) -> Self {
- Self(number)
- }
-}
-
-/// A wrapper of Number, but the value >= 1.
-pub type GreaterThanOrEqualToOneNumber = GreaterThanOrEqualToOne<CSSFloat>;
-
-impl ToAnimatedValue for GreaterThanOrEqualToOneNumber {
- type AnimatedValue = CSSFloat;
-
- #[inline]
- fn to_animated_value(self) -> Self::AnimatedValue {
- self.0
- }
-
- #[inline]
- fn from_animated_value(animated: Self::AnimatedValue) -> Self {
- animated.max(1.).into()
- }
-}
-
-impl From<CSSFloat> for GreaterThanOrEqualToOneNumber {
- #[inline]
- fn from(number: CSSFloat) -> GreaterThanOrEqualToOneNumber {
- GreaterThanOrEqualToOne::<CSSFloat>(number)
- }
-}
-
-impl From<GreaterThanOrEqualToOneNumber> for CSSFloat {
- #[inline]
- fn from(number: GreaterThanOrEqualToOneNumber) -> CSSFloat {
- number.0
- }
-}
-
-#[allow(missing_docs)]
-#[derive(
- Animate,
- Clone,
- ComputeSquaredDistance,
- Copy,
- Debug,
- MallocSizeOf,
- PartialEq,
- ToAnimatedZero,
- ToCss,
- ToResolvedValue,
-)]
-#[repr(C, u8)]
-pub enum NumberOrPercentage {
- Percentage(Percentage),
- Number(Number),
-}
-
-impl NumberOrPercentage {
- fn clamp_to_non_negative(self) -> Self {
- match self {
- NumberOrPercentage::Percentage(p) => {
- NumberOrPercentage::Percentage(p.clamp_to_non_negative())
- },
- NumberOrPercentage::Number(n) => NumberOrPercentage::Number(n.max(0.)),
- }
- }
-}
-
-impl ToComputedValue for specified::NumberOrPercentage {
- type ComputedValue = NumberOrPercentage;
-
- #[inline]
- fn to_computed_value(&self, context: &Context) -> NumberOrPercentage {
- match *self {
- specified::NumberOrPercentage::Percentage(percentage) => {
- NumberOrPercentage::Percentage(percentage.to_computed_value(context))
- },
- specified::NumberOrPercentage::Number(number) => {
- NumberOrPercentage::Number(number.to_computed_value(context))
- },
- }
- }
- #[inline]
- fn from_computed_value(computed: &NumberOrPercentage) -> Self {
- match *computed {
- NumberOrPercentage::Percentage(percentage) => {
- specified::NumberOrPercentage::Percentage(ToComputedValue::from_computed_value(
- &percentage,
- ))
- },
- NumberOrPercentage::Number(number) => {
- specified::NumberOrPercentage::Number(ToComputedValue::from_computed_value(&number))
- },
- }
- }
-}
-
-/// A non-negative <number-percentage>.
-pub type NonNegativeNumberOrPercentage = NonNegative<NumberOrPercentage>;
-
-impl NonNegativeNumberOrPercentage {
- /// Returns the `100%` value.
- #[inline]
- pub fn hundred_percent() -> Self {
- NonNegative(NumberOrPercentage::Percentage(Percentage::hundred()))
- }
-}
-
-impl ToAnimatedValue for NonNegativeNumberOrPercentage {
- type AnimatedValue = NumberOrPercentage;
-
- #[inline]
- fn to_animated_value(self) -> Self::AnimatedValue {
- self.0
- }
-
- #[inline]
- fn from_animated_value(animated: Self::AnimatedValue) -> Self {
- NonNegative(animated.clamp_to_non_negative())
- }
-}
-
-/// A type used for opacity.
-pub type Opacity = CSSFloat;
-
-/// A `<integer>` value.
-pub type Integer = CSSInteger;
-
-/// A wrapper of Integer, but only accept a value >= 1.
-pub type PositiveInteger = GreaterThanOrEqualToOne<CSSInteger>;
-
-impl ToAnimatedValue for PositiveInteger {
- type AnimatedValue = CSSInteger;
-
- #[inline]
- fn to_animated_value(self) -> Self::AnimatedValue {
- self.0
- }
-
- #[inline]
- fn from_animated_value(animated: Self::AnimatedValue) -> Self {
- cmp::max(animated, 1).into()
- }
-}
-
-impl From<CSSInteger> for PositiveInteger {
- #[inline]
- fn from(int: CSSInteger) -> PositiveInteger {
- GreaterThanOrEqualToOne::<CSSInteger>(int)
- }
-}
-
-/// rect(...) | auto
-pub type ClipRect = generics::GenericClipRect<LengthOrAuto>;
-
-/// rect(...) | auto
-pub type ClipRectOrAuto = generics::GenericClipRectOrAuto<ClipRect>;
-
-/// The computed value of a grid `<track-breadth>`
-pub type TrackBreadth = GenericTrackBreadth<LengthPercentage>;
-
-/// The computed value of a grid `<track-size>`
-pub type TrackSize = GenericTrackSize<LengthPercentage>;
-
-/// The computed value of a grid `<track-size>+`
-pub type ImplicitGridTracks = GenericImplicitGridTracks<TrackSize>;
-
-/// The computed value of a grid `<track-list>`
-/// (could also be `<auto-track-list>` or `<explicit-track-list>`)
-pub type TrackList = GenericTrackList<LengthPercentage, Integer>;
-
-/// The computed value of a `<grid-line>`.
-pub type GridLine = GenericGridLine<Integer>;
-
-/// `<grid-template-rows> | <grid-template-columns>`
-pub type GridTemplateComponent = GenericGridTemplateComponent<LengthPercentage, Integer>;
-
-impl ClipRect {
- /// Given a border box, resolves the clip rect against the border box
- /// in the same space the border box is in
- pub fn for_border_rect<T: Copy + From<Length> + Add<Output = T> + Sub<Output = T>, U>(
- &self,
- border_box: Rect<T, U>,
- ) -> Rect<T, U> {
- fn extract_clip_component<T: From<Length>>(p: &LengthOrAuto, or: T) -> T {
- match *p {
- LengthOrAuto::Auto => or,
- LengthOrAuto::LengthPercentage(ref length) => T::from(*length),
- }
- }
-
- let clip_origin = Point2D::new(
- From::from(self.left.auto_is(|| Length::new(0.))),
- From::from(self.top.auto_is(|| Length::new(0.))),
- );
- let right = extract_clip_component(&self.right, border_box.size.width);
- let bottom = extract_clip_component(&self.bottom, border_box.size.height);
- let clip_size = Size2D::new(right - clip_origin.x, bottom - clip_origin.y);
-
- Rect::new(clip_origin, clip_size).translate(border_box.origin.to_vector())
- }
-}
diff --git a/components/style/values/computed/motion.rs b/components/style/values/computed/motion.rs
deleted file mode 100644
index 8fd578bb72c..00000000000
--- a/components/style/values/computed/motion.rs
+++ /dev/null
@@ -1,65 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
-
-//! Computed types for CSS values that are related to motion path.
-
-use crate::values::computed::{Angle, LengthPercentage, Position};
-use crate::values::generics::motion::{
- GenericOffsetPath, GenericOffsetPosition, GenericRayFunction,
-};
-use crate::Zero;
-
-/// The computed value of ray() function.
-pub type RayFunction = GenericRayFunction<Angle, Position>;
-
-/// The computed value of `offset-path`.
-pub type OffsetPath = GenericOffsetPath<RayFunction>;
-
-/// The computed value of `offset-position`.
-pub type OffsetPosition = GenericOffsetPosition<LengthPercentage, LengthPercentage>;
-
-#[inline]
-fn is_auto_zero_angle(auto: &bool, angle: &Angle) -> bool {
- *auto && angle.is_zero()
-}
-
-/// A computed offset-rotate.
-#[derive(
- Animate,
- Clone,
- ComputeSquaredDistance,
- Copy,
- Debug,
- Deserialize,
- MallocSizeOf,
- PartialEq,
- Serialize,
- ToAnimatedZero,
- ToCss,
- ToResolvedValue,
-)]
-#[repr(C)]
-pub struct OffsetRotate {
- /// If auto is false, this is a fixed angle which indicates a
- /// constant clockwise rotation transformation applied to it by this
- /// specified rotation angle. Otherwise, the angle will be added to
- /// the angle of the direction in layout.
- #[animation(constant)]
- #[css(represents_keyword)]
- pub auto: bool,
- /// The angle value.
- #[css(contextual_skip_if = "is_auto_zero_angle")]
- pub angle: Angle,
-}
-
-impl OffsetRotate {
- /// Returns "auto 0deg".
- #[inline]
- pub fn auto() -> Self {
- OffsetRotate {
- auto: true,
- angle: Zero::zero(),
- }
- }
-}
diff --git a/components/style/values/computed/outline.rs b/components/style/values/computed/outline.rs
deleted file mode 100644
index f872176529f..00000000000
--- a/components/style/values/computed/outline.rs
+++ /dev/null
@@ -1,7 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
-
-//! Computed values for outline properties
-
-pub use crate::values::specified::OutlineStyle;
diff --git a/components/style/values/computed/page.rs b/components/style/values/computed/page.rs
deleted file mode 100644
index 6f71c912cfb..00000000000
--- a/components/style/values/computed/page.rs
+++ /dev/null
@@ -1,75 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
-
-//! Computed @page at-rule properties and named-page style properties
-
-use crate::values::computed::length::NonNegativeLength;
-use crate::values::computed::{Context, ToComputedValue};
-use crate::values::generics;
-use crate::values::generics::size::Size2D;
-
-use crate::values::specified::page as specified;
-pub use generics::page::GenericPageSize;
-pub use generics::page::PageOrientation;
-pub use generics::page::PageSizeOrientation;
-pub use generics::page::PaperSize;
-pub use specified::PageName;
-
-/// Computed value of the @page size descriptor
-///
-/// The spec says that the computed value should be the same as the specified
-/// value but with all absolute units, but it's not currently possibly observe
-/// the computed value of page-size.
-#[derive(Clone, Copy, Debug, MallocSizeOf, PartialEq, ToCss, ToResolvedValue, ToShmem)]
-#[repr(C, u8)]
-pub enum PageSize {
- /// Specified size, paper size, or paper size and orientation.
- Size(Size2D<NonNegativeLength>),
- /// `landscape` or `portrait` value, no specified size.
- Orientation(PageSizeOrientation),
- /// `auto` value
- Auto,
-}
-
-impl ToComputedValue for specified::PageSize {
- type ComputedValue = PageSize;
-
- fn to_computed_value(&self, ctx: &Context) -> Self::ComputedValue {
- match &*self {
- Self::Size(s) => PageSize::Size(s.to_computed_value(ctx)),
- Self::PaperSize(p, PageSizeOrientation::Landscape) => PageSize::Size(Size2D {
- width: p.long_edge().to_computed_value(ctx),
- height: p.short_edge().to_computed_value(ctx),
- }),
- Self::PaperSize(p, PageSizeOrientation::Portrait) => PageSize::Size(Size2D {
- width: p.short_edge().to_computed_value(ctx),
- height: p.long_edge().to_computed_value(ctx),
- }),
- Self::Orientation(o) => PageSize::Orientation(*o),
- Self::Auto => PageSize::Auto,
- }
- }
-
- fn from_computed_value(computed: &Self::ComputedValue) -> Self {
- match *computed {
- PageSize::Size(s) => Self::Size(ToComputedValue::from_computed_value(&s)),
- PageSize::Orientation(o) => Self::Orientation(o),
- PageSize::Auto => Self::Auto,
- }
- }
-}
-
-impl PageSize {
- /// `auto` value.
- #[inline]
- pub fn auto() -> Self {
- PageSize::Auto
- }
-
- /// Whether this is the `auto` value.
- #[inline]
- pub fn is_auto(&self) -> bool {
- matches!(*self, PageSize::Auto)
- }
-}
diff --git a/components/style/values/computed/percentage.rs b/components/style/values/computed/percentage.rs
deleted file mode 100644
index 994c01594a3..00000000000
--- a/components/style/values/computed/percentage.rs
+++ /dev/null
@@ -1,136 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
-
-//! Computed percentages.
-
-use crate::values::animated::ToAnimatedValue;
-use crate::values::generics::NonNegative;
-use crate::values::specified::percentage::ToPercentage;
-use crate::values::{serialize_normalized_percentage, CSSFloat};
-use crate::Zero;
-use std::fmt;
-use style_traits::{CssWriter, ToCss};
-
-/// A computed percentage.
-#[derive(
- Animate,
- Clone,
- ComputeSquaredDistance,
- Copy,
- Debug,
- Default,
- Deserialize,
- MallocSizeOf,
- PartialEq,
- PartialOrd,
- Serialize,
- SpecifiedValueInfo,
- ToAnimatedValue,
- ToAnimatedZero,
- ToComputedValue,
- ToResolvedValue,
- ToShmem,
-)]
-#[repr(C)]
-pub struct Percentage(pub CSSFloat);
-
-impl Percentage {
- /// 100%
- #[inline]
- pub fn hundred() -> Self {
- Percentage(1.)
- }
-
- /// Returns the absolute value for this percentage.
- #[inline]
- pub fn abs(&self) -> Self {
- Percentage(self.0.abs())
- }
-
- /// Clamps this percentage to a non-negative percentage.
- #[inline]
- pub fn clamp_to_non_negative(self) -> Self {
- Percentage(self.0.max(0.))
- }
-}
-
-impl Zero for Percentage {
- fn zero() -> Self {
- Percentage(0.)
- }
-
- fn is_zero(&self) -> bool {
- self.0 == 0.
- }
-}
-
-impl ToPercentage for Percentage {
- fn to_percentage(&self) -> CSSFloat {
- self.0
- }
-}
-
-impl std::ops::AddAssign for Percentage {
- fn add_assign(&mut self, other: Self) {
- self.0 += other.0
- }
-}
-
-impl std::ops::Add for Percentage {
- type Output = Self;
-
- fn add(self, other: Self) -> Self {
- Percentage(self.0 + other.0)
- }
-}
-
-impl std::ops::Sub for Percentage {
- type Output = Self;
-
- fn sub(self, other: Self) -> Self {
- Percentage(self.0 - other.0)
- }
-}
-
-impl std::ops::Rem for Percentage {
- type Output = Self;
-
- fn rem(self, other: Self) -> Self {
- Percentage(self.0 % other.0)
- }
-}
-
-impl ToCss for Percentage {
- fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
- where
- W: fmt::Write,
- {
- serialize_normalized_percentage(self.0, dest)
- }
-}
-
-/// A wrapper over a `Percentage`, whose value should be clamped to 0.
-pub type NonNegativePercentage = NonNegative<Percentage>;
-
-impl NonNegativePercentage {
- /// 100%
- #[inline]
- pub fn hundred() -> Self {
- NonNegative(Percentage::hundred())
- }
-}
-
-impl ToAnimatedValue for NonNegativePercentage {
- type AnimatedValue = Percentage;
-
- #[inline]
- fn to_animated_value(self) -> Self::AnimatedValue {
- self.0
- }
-
- #[inline]
- fn from_animated_value(animated: Self::AnimatedValue) -> Self {
- NonNegative(animated.clamp_to_non_negative())
- }
-}
diff --git a/components/style/values/computed/position.rs b/components/style/values/computed/position.rs
deleted file mode 100644
index 5a10c0f23d7..00000000000
--- a/components/style/values/computed/position.rs
+++ /dev/null
@@ -1,74 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
-
-//! CSS handling for the computed value of
-//! [`position`][position] values.
-//!
-//! [position]: https://drafts.csswg.org/css-backgrounds-3/#position
-
-use crate::values::computed::{Integer, LengthPercentage, NonNegativeNumber, Percentage};
-use crate::values::generics::position::AspectRatio as GenericAspectRatio;
-use crate::values::generics::position::Position as GenericPosition;
-use crate::values::generics::position::PositionComponent as GenericPositionComponent;
-use crate::values::generics::position::PositionOrAuto as GenericPositionOrAuto;
-use crate::values::generics::position::ZIndex as GenericZIndex;
-pub use crate::values::specified::position::{GridAutoFlow, GridTemplateAreas, MasonryAutoFlow};
-use crate::Zero;
-use std::fmt::{self, Write};
-use style_traits::{CssWriter, ToCss};
-
-/// The computed value of a CSS `<position>`
-pub type Position = GenericPosition<HorizontalPosition, VerticalPosition>;
-
-/// The computed value of an `auto | <position>`
-pub type PositionOrAuto = GenericPositionOrAuto<Position>;
-
-/// The computed value of a CSS horizontal position.
-pub type HorizontalPosition = LengthPercentage;
-
-/// The computed value of a CSS vertical position.
-pub type VerticalPosition = LengthPercentage;
-
-impl Position {
- /// `50% 50%`
- #[inline]
- pub fn center() -> Self {
- Self::new(
- LengthPercentage::new_percent(Percentage(0.5)),
- LengthPercentage::new_percent(Percentage(0.5)),
- )
- }
-
- /// `0% 0%`
- #[inline]
- pub fn zero() -> Self {
- Self::new(LengthPercentage::zero(), LengthPercentage::zero())
- }
-}
-
-impl ToCss for Position {
- fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
- where
- W: Write,
- {
- self.horizontal.to_css(dest)?;
- dest.write_char(' ')?;
- self.vertical.to_css(dest)
- }
-}
-
-impl GenericPositionComponent for LengthPercentage {
- fn is_center(&self) -> bool {
- match self.to_percentage() {
- Some(Percentage(per)) => per == 0.5,
- _ => false,
- }
- }
-}
-
-/// A computed value for the `z-index` property.
-pub type ZIndex = GenericZIndex<Integer>;
-
-/// A computed value for the `aspect-ratio` property.
-pub type AspectRatio = GenericAspectRatio<NonNegativeNumber>;
diff --git a/components/style/values/computed/ratio.rs b/components/style/values/computed/ratio.rs
deleted file mode 100644
index ae8997cfc06..00000000000
--- a/components/style/values/computed/ratio.rs
+++ /dev/null
@@ -1,115 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
-
-//! `<ratio>` computed values.
-
-use crate::values::animated::{Animate, Procedure};
-use crate::values::computed::NonNegativeNumber;
-use crate::values::distance::{ComputeSquaredDistance, SquaredDistance};
-use crate::values::generics::ratio::Ratio as GenericRatio;
-use crate::{One, Zero};
-use std::cmp::{Ordering, PartialOrd};
-
-/// A computed <ratio> value.
-pub type Ratio = GenericRatio<NonNegativeNumber>;
-
-impl PartialOrd for Ratio {
- fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
- f64::partial_cmp(
- &((self.0).0 as f64 * (other.1).0 as f64),
- &((self.1).0 as f64 * (other.0).0 as f64),
- )
- }
-}
-
-/// https://drafts.csswg.org/css-values/#combine-ratio
-impl Animate for Ratio {
- fn animate(&self, other: &Self, procedure: Procedure) -> Result<Self, ()> {
- // If either <ratio> is degenerate, the values cannot be interpolated.
- if self.is_degenerate() || other.is_degenerate() {
- return Err(());
- }
-
- // Addition of <ratio>s is not possible, and based on
- // https://drafts.csswg.org/css-values-4/#not-additive,
- // we simply use the first value as the result value.
- // Besides, the procedure for accumulation should be identical to addition here.
- if matches!(procedure, Procedure::Add | Procedure::Accumulate { .. }) {
- return Ok(self.clone());
- }
-
- // The interpolation of a <ratio> is defined by converting each <ratio> to a number by
- // dividing the first value by the second (so a ratio of 3 / 2 would become 1.5), taking
- // the logarithm of that result (so the 1.5 would become approximately 0.176), then
- // interpolating those values.
- //
- // The result during the interpolation is converted back to a <ratio> by inverting the
- // logarithm, then interpreting the result as a <ratio> with the result as the first value
- // and 1 as the second value.
- let start = self.to_f32().ln();
- let end = other.to_f32().ln();
- let e = std::f32::consts::E;
- let result = e.powf(start.animate(&end, procedure)?);
- // The range of the result is [0, inf), based on the easing function.
- if result.is_zero() || result.is_infinite() {
- return Err(());
- }
- Ok(Ratio::new(result, 1.0f32))
- }
-}
-
-impl ComputeSquaredDistance for Ratio {
- fn compute_squared_distance(&self, other: &Self) -> Result<SquaredDistance, ()> {
- if self.is_degenerate() || other.is_degenerate() {
- return Err(());
- }
- // Use the distance of their logarithm values. (This is used by testing, so don't need to
- // care about the base. Here we use the same base as that in animate().)
- self.to_f32()
- .ln()
- .compute_squared_distance(&other.to_f32().ln())
- }
-}
-
-impl Zero for Ratio {
- fn zero() -> Self {
- Self::new(Zero::zero(), One::one())
- }
-
- fn is_zero(&self) -> bool {
- self.0.is_zero()
- }
-}
-
-impl Ratio {
- /// Returns a new Ratio.
- #[inline]
- pub fn new(a: f32, b: f32) -> Self {
- GenericRatio(a.into(), b.into())
- }
-
- /// Returns the used value. A ratio of 0/0 behaves as the ratio 1/0.
- /// https://drafts.csswg.org/css-values-4/#ratios
- pub fn used_value(self) -> Self {
- if self.0.is_zero() && self.1.is_zero() {
- Ratio::new(One::one(), Zero::zero())
- } else {
- self
- }
- }
-
- /// Returns true if this is a degenerate ratio.
- /// https://drafts.csswg.org/css-values/#degenerate-ratio
- #[inline]
- pub fn is_degenerate(&self) -> bool {
- self.0.is_zero() || self.1.is_zero()
- }
-
- /// Returns the f32 value by dividing the first value by the second one.
- #[inline]
- fn to_f32(&self) -> f32 {
- debug_assert!(!self.is_degenerate());
- (self.0).0 / (self.1).0
- }
-}
diff --git a/components/style/values/computed/rect.rs b/components/style/values/computed/rect.rs
deleted file mode 100644
index ec44360fc81..00000000000
--- a/components/style/values/computed/rect.rs
+++ /dev/null
@@ -1,11 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
-
-//! Computed types for CSS borders.
-
-use crate::values::computed::length::NonNegativeLengthOrNumber;
-use crate::values::generics::rect::Rect;
-
-/// A specified rectangle made of four `<length-or-number>` values.
-pub type NonNegativeLengthOrNumberRect = Rect<NonNegativeLengthOrNumber>;
diff --git a/components/style/values/computed/resolution.rs b/components/style/values/computed/resolution.rs
deleted file mode 100644
index 28153911809..00000000000
--- a/components/style/values/computed/resolution.rs
+++ /dev/null
@@ -1,56 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
-
-//! Resolution values:
-//!
-//! https://drafts.csswg.org/css-values/#resolution
-
-use crate::values::computed::{Context, ToComputedValue};
-use crate::values::specified;
-use crate::values::CSSFloat;
-use std::fmt::{self, Write};
-use style_traits::{CssWriter, ToCss};
-
-/// A computed `<resolution>`.
-#[repr(C)]
-#[derive(Clone, Debug, MallocSizeOf, PartialEq, ToResolvedValue, ToShmem)]
-pub struct Resolution(CSSFloat);
-
-impl Resolution {
- /// Returns this resolution value as dppx.
- #[inline]
- pub fn dppx(&self) -> CSSFloat {
- self.0
- }
-
- /// Return a computed `resolution` value from a dppx float value.
- #[inline]
- pub fn from_dppx(dppx: CSSFloat) -> Self {
- Resolution(dppx)
- }
-}
-
-impl ToComputedValue for specified::Resolution {
- type ComputedValue = Resolution;
-
- #[inline]
- fn to_computed_value(&self, _: &Context) -> Self::ComputedValue {
- Resolution(crate::values::normalize(self.dppx().max(0.0)))
- }
-
- #[inline]
- fn from_computed_value(computed: &Self::ComputedValue) -> Self {
- specified::Resolution::from_dppx(computed.dppx())
- }
-}
-
-impl ToCss for Resolution {
- fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
- where
- W: fmt::Write,
- {
- self.dppx().to_css(dest)?;
- dest.write_str("dppx")
- }
-}
diff --git a/components/style/values/computed/svg.rs b/components/style/values/computed/svg.rs
deleted file mode 100644
index 640c3bfda70..00000000000
--- a/components/style/values/computed/svg.rs
+++ /dev/null
@@ -1,68 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
-
-//! Computed types for SVG properties.
-
-use crate::values::computed::color::Color;
-use crate::values::computed::url::ComputedUrl;
-use crate::values::computed::{LengthPercentage, NonNegativeLengthPercentage, Opacity};
-use crate::values::generics::svg as generic;
-use crate::Zero;
-
-pub use crate::values::specified::{DProperty, MozContextProperties, SVGPaintOrder};
-
-/// Computed SVG Paint value
-pub type SVGPaint = generic::GenericSVGPaint<Color, ComputedUrl>;
-
-/// Computed SVG Paint Kind value
-pub type SVGPaintKind = generic::GenericSVGPaintKind<Color, ComputedUrl>;
-
-impl SVGPaint {
- /// Opaque black color
- pub fn black() -> Self {
- SVGPaint {
- kind: generic::SVGPaintKind::Color(Color::black()),
- fallback: generic::SVGPaintFallback::Unset,
- }
- }
-}
-
-/// <length> | <percentage> | <number> | context-value
-pub type SVGLength = generic::GenericSVGLength<LengthPercentage>;
-
-impl SVGLength {
- /// `0px`
- pub fn zero() -> Self {
- generic::SVGLength::LengthPercentage(LengthPercentage::zero())
- }
-}
-
-/// An non-negative wrapper of SVGLength.
-pub type SVGWidth = generic::GenericSVGLength<NonNegativeLengthPercentage>;
-
-impl SVGWidth {
- /// `1px`.
- pub fn one() -> Self {
- use crate::values::generics::NonNegative;
- generic::SVGLength::LengthPercentage(NonNegative(LengthPercentage::one()))
- }
-}
-
-/// [ <length> | <percentage> | <number> ]# | context-value
-pub type SVGStrokeDashArray = generic::GenericSVGStrokeDashArray<NonNegativeLengthPercentage>;
-
-impl Default for SVGStrokeDashArray {
- fn default() -> Self {
- generic::SVGStrokeDashArray::Values(Default::default())
- }
-}
-
-/// <opacity-value> | context-fill-opacity | context-stroke-opacity
-pub type SVGOpacity = generic::GenericSVGOpacity<Opacity>;
-
-impl Default for SVGOpacity {
- fn default() -> Self {
- generic::SVGOpacity::Opacity(1.)
- }
-}
diff --git a/components/style/values/computed/table.rs b/components/style/values/computed/table.rs
deleted file mode 100644
index 47109e20eca..00000000000
--- a/components/style/values/computed/table.rs
+++ /dev/null
@@ -1,7 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
-
-//! Computed types for CSS values related to tables.
-
-pub use super::specified::table::CaptionSide;
diff --git a/components/style/values/computed/text.rs b/components/style/values/computed/text.rs
deleted file mode 100644
index 3c9d13031f2..00000000000
--- a/components/style/values/computed/text.rs
+++ /dev/null
@@ -1,262 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
-
-//! Computed types for text properties.
-
-#[cfg(feature = "servo")]
-use crate::properties::StyleBuilder;
-use crate::values::computed::length::{Length, LengthPercentage};
-use crate::values::computed::{Context, NonNegativeLength, NonNegativeNumber, ToComputedValue};
-use crate::values::generics::text::InitialLetter as GenericInitialLetter;
-use crate::values::generics::text::LineHeight as GenericLineHeight;
-use crate::values::generics::text::{GenericTextDecorationLength, Spacing};
-use crate::values::resolved::{Context as ResolvedContext, ToResolvedValue};
-use crate::values::specified::text::{self as specified, TextOverflowSide};
-use crate::values::specified::text::{TextEmphasisFillMode, TextEmphasisShapeKeyword};
-use crate::values::{CSSFloat, CSSInteger};
-use crate::Zero;
-use std::fmt::{self, Write};
-use style_traits::{CssWriter, ToCss};
-
-pub use crate::values::specified::text::{
- MozControlCharacterVisibility, TextAlignLast, TextUnderlinePosition,
-};
-pub use crate::values::specified::HyphenateCharacter;
-pub use crate::values::specified::{LineBreak, OverflowWrap, RubyPosition, WordBreak};
-pub use crate::values::specified::{TextDecorationLine, TextEmphasisPosition};
-pub use crate::values::specified::{TextDecorationSkipInk, TextJustify, TextTransform};
-
-/// A computed value for the `initial-letter` property.
-pub type InitialLetter = GenericInitialLetter<CSSFloat, CSSInteger>;
-
-/// Implements type for `text-decoration-thickness` property.
-pub type TextDecorationLength = GenericTextDecorationLength<LengthPercentage>;
-
-/// The computed value of `text-align`.
-pub type TextAlign = specified::TextAlignKeyword;
-
-/// A computed value for the `letter-spacing` property.
-#[repr(transparent)]
-#[derive(
- Animate,
- Clone,
- ComputeSquaredDistance,
- Copy,
- Debug,
- MallocSizeOf,
- PartialEq,
- ToAnimatedValue,
- ToAnimatedZero,
- ToResolvedValue,
-)]
-pub struct LetterSpacing(pub Length);
-
-impl LetterSpacing {
- /// Return the `normal` computed value, which is just zero.
- #[inline]
- pub fn normal() -> Self {
- LetterSpacing(Length::zero())
- }
-}
-
-impl ToCss for LetterSpacing {
- fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
- where
- W: Write,
- {
- // https://drafts.csswg.org/css-text/#propdef-letter-spacing
- //
- // For legacy reasons, a computed letter-spacing of zero yields a
- // resolved value (getComputedStyle() return value) of normal.
- if self.0.is_zero() {
- return dest.write_str("normal");
- }
- self.0.to_css(dest)
- }
-}
-
-impl ToComputedValue for specified::LetterSpacing {
- type ComputedValue = LetterSpacing;
- fn to_computed_value(&self, context: &Context) -> Self::ComputedValue {
- match *self {
- Spacing::Normal => LetterSpacing(Length::zero()),
- Spacing::Value(ref v) => LetterSpacing(v.to_computed_value(context)),
- }
- }
-
- fn from_computed_value(computed: &Self::ComputedValue) -> Self {
- if computed.0.is_zero() {
- return Spacing::Normal;
- }
- Spacing::Value(ToComputedValue::from_computed_value(&computed.0))
- }
-}
-
-/// A computed value for the `word-spacing` property.
-pub type WordSpacing = LengthPercentage;
-
-impl ToComputedValue for specified::WordSpacing {
- type ComputedValue = WordSpacing;
-
- fn to_computed_value(&self, context: &Context) -> Self::ComputedValue {
- match *self {
- Spacing::Normal => LengthPercentage::zero(),
- Spacing::Value(ref v) => v.to_computed_value(context),
- }
- }
-
- fn from_computed_value(computed: &Self::ComputedValue) -> Self {
- Spacing::Value(ToComputedValue::from_computed_value(computed))
- }
-}
-
-/// A computed value for the `line-height` property.
-pub type LineHeight = GenericLineHeight<NonNegativeNumber, NonNegativeLength>;
-
-impl ToResolvedValue for LineHeight {
- type ResolvedValue = Self;
-
- fn to_resolved_value(self, context: &ResolvedContext) -> Self::ResolvedValue {
- // Resolve <number> to an absolute <length> based on font size.
- #[cfg(feature = "gecko")] {
- if matches!(self, Self::Normal | Self::MozBlockHeight) {
- return self;
- }
- let wm = context.style.writing_mode;
- let vertical = wm.is_vertical() && !wm.is_sideways();
- return Self::Length(context.device.calc_line_height(
- &self,
- vertical,
- context.style.get_font(),
- Some(context.element_info.element),
- ));
- }
- if let LineHeight::Number(num) = &self {
- let size = context.style.get_font().clone_font_size().computed_size();
- LineHeight::Length(NonNegativeLength::new(size.px() * num.0))
- } else {
- self
- }
- }
-
- #[inline]
- fn from_resolved_value(value: Self::ResolvedValue) -> Self {
- value
- }
-}
-
-impl WordSpacing {
- /// Return the `normal` computed value, which is just zero.
- #[inline]
- pub fn normal() -> Self {
- LengthPercentage::zero()
- }
-}
-
-#[derive(Clone, Debug, MallocSizeOf, PartialEq, ToResolvedValue)]
-#[repr(C)]
-/// text-overflow.
-/// When the specified value only has one side, that's the "second"
-/// side, and the sides are logical, so "second" means "end". The
-/// start side is Clip in that case.
-///
-/// When the specified value has two sides, those are our "first"
-/// and "second" sides, and they are physical sides ("left" and
-/// "right").
-pub struct TextOverflow {
- /// First side
- pub first: TextOverflowSide,
- /// Second side
- pub second: TextOverflowSide,
- /// True if the specified value only has one side.
- pub sides_are_logical: bool,
-}
-
-impl TextOverflow {
- /// Returns the initial `text-overflow` value
- pub fn get_initial_value() -> TextOverflow {
- TextOverflow {
- first: TextOverflowSide::Clip,
- second: TextOverflowSide::Clip,
- sides_are_logical: true,
- }
- }
-}
-
-impl ToCss for TextOverflow {
- fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
- where
- W: Write,
- {
- if self.sides_are_logical {
- debug_assert_eq!(self.first, TextOverflowSide::Clip);
- self.second.to_css(dest)?;
- } else {
- self.first.to_css(dest)?;
- dest.write_char(' ')?;
- self.second.to_css(dest)?;
- }
- Ok(())
- }
-}
-
-/// A struct that represents the _used_ value of the text-decoration property.
-///
-/// FIXME(emilio): This is done at style resolution time, though probably should
-/// be done at layout time, otherwise we need to account for display: contents
-/// and similar stuff when we implement it.
-///
-/// FIXME(emilio): Also, should be just a bitfield instead of three bytes.
-#[derive(Clone, Copy, Debug, Default, MallocSizeOf, PartialEq, ToResolvedValue)]
-pub struct TextDecorationsInEffect {
- /// Whether an underline is in effect.
- pub underline: bool,
- /// Whether an overline decoration is in effect.
- pub overline: bool,
- /// Whether a line-through style is in effect.
- pub line_through: bool,
-}
-
-impl TextDecorationsInEffect {
- /// Computes the text-decorations in effect for a given style.
- #[cfg(feature = "servo")]
- pub fn from_style(style: &StyleBuilder) -> Self {
- // Start with no declarations if this is an atomic inline-level box;
- // otherwise, start with the declarations in effect and add in the text
- // decorations that this block specifies.
- let mut result = if style.get_box().clone_display().is_atomic_inline_level() {
- Self::default()
- } else {
- style
- .get_parent_inherited_text()
- .text_decorations_in_effect
- .clone()
- };
-
- let line = style.get_text().clone_text_decoration_line();
-
- result.underline |= line.contains(TextDecorationLine::UNDERLINE);
- result.overline |= line.contains(TextDecorationLine::OVERLINE);
- result.line_through |= line.contains(TextDecorationLine::LINE_THROUGH);
-
- result
- }
-}
-
-/// Computed value for the text-emphasis-style property
-#[derive(Clone, Debug, MallocSizeOf, PartialEq, ToCss, ToResolvedValue)]
-#[allow(missing_docs)]
-#[repr(C, u8)]
-pub enum TextEmphasisStyle {
- /// [ <fill> || <shape> ]
- Keyword {
- #[css(skip_if = "TextEmphasisFillMode::is_filled")]
- fill: TextEmphasisFillMode,
- shape: TextEmphasisShapeKeyword,
- },
- /// `none`
- None,
- /// `<string>` (of which only the first grapheme cluster will be used).
- String(crate::OwnedStr),
-}
diff --git a/components/style/values/computed/time.rs b/components/style/values/computed/time.rs
deleted file mode 100644
index f542333a823..00000000000
--- a/components/style/values/computed/time.rs
+++ /dev/null
@@ -1,45 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
-
-//! Computed time values.
-
-use crate::values::CSSFloat;
-use std::fmt::{self, Write};
-use style_traits::{CssWriter, ToCss};
-
-/// A computed `<time>` value.
-#[derive(Clone, Copy, Debug, MallocSizeOf, PartialEq, PartialOrd, ToResolvedValue)]
-#[cfg_attr(feature = "servo", derive(Deserialize, Serialize))]
-#[repr(C)]
-pub struct Time {
- seconds: CSSFloat,
-}
-
-impl Time {
- /// Creates a time value from a seconds amount.
- pub fn from_seconds(seconds: CSSFloat) -> Self {
- Time { seconds }
- }
-
- /// Returns `0s`.
- pub fn zero() -> Self {
- Self::from_seconds(0.0)
- }
-
- /// Returns the amount of seconds this time represents.
- #[inline]
- pub fn seconds(&self) -> CSSFloat {
- self.seconds
- }
-}
-
-impl ToCss for Time {
- fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
- where
- W: Write,
- {
- self.seconds().to_css(dest)?;
- dest.write_char('s')
- }
-}
diff --git a/components/style/values/computed/transform.rs b/components/style/values/computed/transform.rs
deleted file mode 100644
index d70349ee0fe..00000000000
--- a/components/style/values/computed/transform.rs
+++ /dev/null
@@ -1,558 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
-
-//! Computed types for CSS values that are related to transformations.
-
-use super::CSSFloat;
-use crate::values::animated::transform::{Perspective, Scale3D, Translate3D};
-use crate::values::animated::ToAnimatedZero;
-use crate::values::computed::{Angle, Integer, Length, LengthPercentage, Number, Percentage};
-use crate::values::generics::transform as generic;
-use crate::Zero;
-use euclid::default::{Transform3D, Vector3D};
-
-pub use crate::values::generics::transform::TransformStyle;
-
-/// A single operation in a computed CSS `transform`
-pub type TransformOperation =
- generic::GenericTransformOperation<Angle, Number, Length, Integer, LengthPercentage>;
-/// A computed CSS `transform`
-pub type Transform = generic::GenericTransform<TransformOperation>;
-
-/// The computed value of a CSS `<transform-origin>`
-pub type TransformOrigin =
- generic::GenericTransformOrigin<LengthPercentage, LengthPercentage, Length>;
-
-/// The computed value of the `perspective()` transform function.
-pub type PerspectiveFunction = generic::PerspectiveFunction<Length>;
-
-/// A vector to represent the direction vector (rotate axis) for Rotate3D.
-pub type DirectionVector = Vector3D<CSSFloat>;
-
-impl TransformOrigin {
- /// Returns the initial computed value for `transform-origin`.
- #[inline]
- pub fn initial_value() -> Self {
- Self::new(
- LengthPercentage::new_percent(Percentage(0.5)),
- LengthPercentage::new_percent(Percentage(0.5)),
- Length::new(0.),
- )
- }
-}
-
-/// computed value of matrix3d()
-pub type Matrix3D = generic::Matrix3D<Number>;
-
-/// computed value of matrix()
-pub type Matrix = generic::Matrix<Number>;
-
-// we rustfmt_skip here because we want the matrices to look like
-// matrices instead of being split across lines
-#[cfg_attr(rustfmt, rustfmt_skip)]
-impl Matrix3D {
- /// Get an identity matrix
- #[inline]
- pub fn identity() -> Self {
- Self {
- m11: 1.0, m12: 0.0, m13: 0.0, m14: 0.0,
- m21: 0.0, m22: 1.0, m23: 0.0, m24: 0.0,
- m31: 0.0, m32: 0.0, m33: 1.0, m34: 0.0,
- m41: 0., m42: 0., m43: 0., m44: 1.0
- }
- }
-
- /// Convert to a 2D Matrix
- #[inline]
- pub fn into_2d(self) -> Result<Matrix, ()> {
- if self.m13 == 0. && self.m23 == 0. &&
- self.m31 == 0. && self.m32 == 0. &&
- self.m33 == 1. && self.m34 == 0. &&
- self.m14 == 0. && self.m24 == 0. &&
- self.m43 == 0. && self.m44 == 1. {
- Ok(Matrix {
- a: self.m11, c: self.m21, e: self.m41,
- b: self.m12, d: self.m22, f: self.m42,
- })
- } else {
- Err(())
- }
- }
-
- /// Return true if this has 3D components.
- #[inline]
- pub fn is_3d(&self) -> bool {
- self.m13 != 0.0 || self.m14 != 0.0 ||
- self.m23 != 0.0 || self.m24 != 0.0 ||
- self.m31 != 0.0 || self.m32 != 0.0 ||
- self.m33 != 1.0 || self.m34 != 0.0 ||
- self.m43 != 0.0 || self.m44 != 1.0
- }
-
- /// Return determinant value.
- #[inline]
- pub fn determinant(&self) -> CSSFloat {
- self.m14 * self.m23 * self.m32 * self.m41 -
- self.m13 * self.m24 * self.m32 * self.m41 -
- self.m14 * self.m22 * self.m33 * self.m41 +
- self.m12 * self.m24 * self.m33 * self.m41 +
- self.m13 * self.m22 * self.m34 * self.m41 -
- self.m12 * self.m23 * self.m34 * self.m41 -
- self.m14 * self.m23 * self.m31 * self.m42 +
- self.m13 * self.m24 * self.m31 * self.m42 +
- self.m14 * self.m21 * self.m33 * self.m42 -
- self.m11 * self.m24 * self.m33 * self.m42 -
- self.m13 * self.m21 * self.m34 * self.m42 +
- self.m11 * self.m23 * self.m34 * self.m42 +
- self.m14 * self.m22 * self.m31 * self.m43 -
- self.m12 * self.m24 * self.m31 * self.m43 -
- self.m14 * self.m21 * self.m32 * self.m43 +
- self.m11 * self.m24 * self.m32 * self.m43 +
- self.m12 * self.m21 * self.m34 * self.m43 -
- self.m11 * self.m22 * self.m34 * self.m43 -
- self.m13 * self.m22 * self.m31 * self.m44 +
- self.m12 * self.m23 * self.m31 * self.m44 +
- self.m13 * self.m21 * self.m32 * self.m44 -
- self.m11 * self.m23 * self.m32 * self.m44 -
- self.m12 * self.m21 * self.m33 * self.m44 +
- self.m11 * self.m22 * self.m33 * self.m44
- }
-
- /// Transpose a matrix.
- #[inline]
- pub fn transpose(&self) -> Self {
- Self {
- m11: self.m11, m12: self.m21, m13: self.m31, m14: self.m41,
- m21: self.m12, m22: self.m22, m23: self.m32, m24: self.m42,
- m31: self.m13, m32: self.m23, m33: self.m33, m34: self.m43,
- m41: self.m14, m42: self.m24, m43: self.m34, m44: self.m44,
- }
- }
-
- /// Return inverse matrix.
- pub fn inverse(&self) -> Result<Matrix3D, ()> {
- let mut det = self.determinant();
-
- if det == 0.0 {
- return Err(());
- }
-
- det = 1.0 / det;
- let x = Matrix3D {
- m11: det *
- (self.m23 * self.m34 * self.m42 - self.m24 * self.m33 * self.m42 +
- self.m24 * self.m32 * self.m43 - self.m22 * self.m34 * self.m43 -
- self.m23 * self.m32 * self.m44 + self.m22 * self.m33 * self.m44),
- m12: det *
- (self.m14 * self.m33 * self.m42 - self.m13 * self.m34 * self.m42 -
- self.m14 * self.m32 * self.m43 + self.m12 * self.m34 * self.m43 +
- self.m13 * self.m32 * self.m44 - self.m12 * self.m33 * self.m44),
- m13: det *
- (self.m13 * self.m24 * self.m42 - self.m14 * self.m23 * self.m42 +
- self.m14 * self.m22 * self.m43 - self.m12 * self.m24 * self.m43 -
- self.m13 * self.m22 * self.m44 + self.m12 * self.m23 * self.m44),
- m14: det *
- (self.m14 * self.m23 * self.m32 - self.m13 * self.m24 * self.m32 -
- self.m14 * self.m22 * self.m33 + self.m12 * self.m24 * self.m33 +
- self.m13 * self.m22 * self.m34 - self.m12 * self.m23 * self.m34),
- m21: det *
- (self.m24 * self.m33 * self.m41 - self.m23 * self.m34 * self.m41 -
- self.m24 * self.m31 * self.m43 + self.m21 * self.m34 * self.m43 +
- self.m23 * self.m31 * self.m44 - self.m21 * self.m33 * self.m44),
- m22: det *
- (self.m13 * self.m34 * self.m41 - self.m14 * self.m33 * self.m41 +
- self.m14 * self.m31 * self.m43 - self.m11 * self.m34 * self.m43 -
- self.m13 * self.m31 * self.m44 + self.m11 * self.m33 * self.m44),
- m23: det *
- (self.m14 * self.m23 * self.m41 - self.m13 * self.m24 * self.m41 -
- self.m14 * self.m21 * self.m43 + self.m11 * self.m24 * self.m43 +
- self.m13 * self.m21 * self.m44 - self.m11 * self.m23 * self.m44),
- m24: det *
- (self.m13 * self.m24 * self.m31 - self.m14 * self.m23 * self.m31 +
- self.m14 * self.m21 * self.m33 - self.m11 * self.m24 * self.m33 -
- self.m13 * self.m21 * self.m34 + self.m11 * self.m23 * self.m34),
- m31: det *
- (self.m22 * self.m34 * self.m41 - self.m24 * self.m32 * self.m41 +
- self.m24 * self.m31 * self.m42 - self.m21 * self.m34 * self.m42 -
- self.m22 * self.m31 * self.m44 + self.m21 * self.m32 * self.m44),
- m32: det *
- (self.m14 * self.m32 * self.m41 - self.m12 * self.m34 * self.m41 -
- self.m14 * self.m31 * self.m42 + self.m11 * self.m34 * self.m42 +
- self.m12 * self.m31 * self.m44 - self.m11 * self.m32 * self.m44),
- m33: det *
- (self.m12 * self.m24 * self.m41 - self.m14 * self.m22 * self.m41 +
- self.m14 * self.m21 * self.m42 - self.m11 * self.m24 * self.m42 -
- self.m12 * self.m21 * self.m44 + self.m11 * self.m22 * self.m44),
- m34: det *
- (self.m14 * self.m22 * self.m31 - self.m12 * self.m24 * self.m31 -
- self.m14 * self.m21 * self.m32 + self.m11 * self.m24 * self.m32 +
- self.m12 * self.m21 * self.m34 - self.m11 * self.m22 * self.m34),
- m41: det *
- (self.m23 * self.m32 * self.m41 - self.m22 * self.m33 * self.m41 -
- self.m23 * self.m31 * self.m42 + self.m21 * self.m33 * self.m42 +
- self.m22 * self.m31 * self.m43 - self.m21 * self.m32 * self.m43),
- m42: det *
- (self.m12 * self.m33 * self.m41 - self.m13 * self.m32 * self.m41 +
- self.m13 * self.m31 * self.m42 - self.m11 * self.m33 * self.m42 -
- self.m12 * self.m31 * self.m43 + self.m11 * self.m32 * self.m43),
- m43: det *
- (self.m13 * self.m22 * self.m41 - self.m12 * self.m23 * self.m41 -
- self.m13 * self.m21 * self.m42 + self.m11 * self.m23 * self.m42 +
- self.m12 * self.m21 * self.m43 - self.m11 * self.m22 * self.m43),
- m44: det *
- (self.m12 * self.m23 * self.m31 - self.m13 * self.m22 * self.m31 +
- self.m13 * self.m21 * self.m32 - self.m11 * self.m23 * self.m32 -
- self.m12 * self.m21 * self.m33 + self.m11 * self.m22 * self.m33),
- };
-
- Ok(x)
- }
-
- /// Multiply `pin * self`.
- #[inline]
- pub fn pre_mul_point4(&self, pin: &[f32; 4]) -> [f32; 4] {
- [
- pin[0] * self.m11 + pin[1] * self.m21 + pin[2] * self.m31 + pin[3] * self.m41,
- pin[0] * self.m12 + pin[1] * self.m22 + pin[2] * self.m32 + pin[3] * self.m42,
- pin[0] * self.m13 + pin[1] * self.m23 + pin[2] * self.m33 + pin[3] * self.m43,
- pin[0] * self.m14 + pin[1] * self.m24 + pin[2] * self.m34 + pin[3] * self.m44,
- ]
- }
-
- /// Return the multiplication of two 4x4 matrices.
- #[inline]
- pub fn multiply(&self, other: &Self) -> Self {
- Matrix3D {
- m11: self.m11 * other.m11 + self.m12 * other.m21 +
- self.m13 * other.m31 + self.m14 * other.m41,
- m12: self.m11 * other.m12 + self.m12 * other.m22 +
- self.m13 * other.m32 + self.m14 * other.m42,
- m13: self.m11 * other.m13 + self.m12 * other.m23 +
- self.m13 * other.m33 + self.m14 * other.m43,
- m14: self.m11 * other.m14 + self.m12 * other.m24 +
- self.m13 * other.m34 + self.m14 * other.m44,
- m21: self.m21 * other.m11 + self.m22 * other.m21 +
- self.m23 * other.m31 + self.m24 * other.m41,
- m22: self.m21 * other.m12 + self.m22 * other.m22 +
- self.m23 * other.m32 + self.m24 * other.m42,
- m23: self.m21 * other.m13 + self.m22 * other.m23 +
- self.m23 * other.m33 + self.m24 * other.m43,
- m24: self.m21 * other.m14 + self.m22 * other.m24 +
- self.m23 * other.m34 + self.m24 * other.m44,
- m31: self.m31 * other.m11 + self.m32 * other.m21 +
- self.m33 * other.m31 + self.m34 * other.m41,
- m32: self.m31 * other.m12 + self.m32 * other.m22 +
- self.m33 * other.m32 + self.m34 * other.m42,
- m33: self.m31 * other.m13 + self.m32 * other.m23 +
- self.m33 * other.m33 + self.m34 * other.m43,
- m34: self.m31 * other.m14 + self.m32 * other.m24 +
- self.m33 * other.m34 + self.m34 * other.m44,
- m41: self.m41 * other.m11 + self.m42 * other.m21 +
- self.m43 * other.m31 + self.m44 * other.m41,
- m42: self.m41 * other.m12 + self.m42 * other.m22 +
- self.m43 * other.m32 + self.m44 * other.m42,
- m43: self.m41 * other.m13 + self.m42 * other.m23 +
- self.m43 * other.m33 + self.m44 * other.m43,
- m44: self.m41 * other.m14 + self.m42 * other.m24 +
- self.m43 * other.m34 + self.m44 * other.m44,
- }
- }
-
- /// Scale the matrix by a factor.
- #[inline]
- pub fn scale_by_factor(&mut self, scaling_factor: CSSFloat) {
- self.m11 *= scaling_factor;
- self.m12 *= scaling_factor;
- self.m13 *= scaling_factor;
- self.m14 *= scaling_factor;
- self.m21 *= scaling_factor;
- self.m22 *= scaling_factor;
- self.m23 *= scaling_factor;
- self.m24 *= scaling_factor;
- self.m31 *= scaling_factor;
- self.m32 *= scaling_factor;
- self.m33 *= scaling_factor;
- self.m34 *= scaling_factor;
- self.m41 *= scaling_factor;
- self.m42 *= scaling_factor;
- self.m43 *= scaling_factor;
- self.m44 *= scaling_factor;
- }
-
- /// Return the matrix 3x3 part (top-left corner).
- /// This is used by retrieving the scale and shear factors
- /// during decomposing a 3d matrix.
- #[inline]
- pub fn get_matrix_3x3_part(&self) -> [[f32; 3]; 3] {
- [
- [ self.m11, self.m12, self.m13 ],
- [ self.m21, self.m22, self.m23 ],
- [ self.m31, self.m32, self.m33 ],
- ]
- }
-
- /// Set perspective on the matrix.
- #[inline]
- pub fn set_perspective(&mut self, perspective: &Perspective) {
- self.m14 = perspective.0;
- self.m24 = perspective.1;
- self.m34 = perspective.2;
- self.m44 = perspective.3;
- }
-
- /// Apply translate on the matrix.
- #[inline]
- pub fn apply_translate(&mut self, translate: &Translate3D) {
- self.m41 += translate.0 * self.m11 + translate.1 * self.m21 + translate.2 * self.m31;
- self.m42 += translate.0 * self.m12 + translate.1 * self.m22 + translate.2 * self.m32;
- self.m43 += translate.0 * self.m13 + translate.1 * self.m23 + translate.2 * self.m33;
- self.m44 += translate.0 * self.m14 + translate.1 * self.m24 + translate.2 * self.m34;
- }
-
- /// Apply scale on the matrix.
- #[inline]
- pub fn apply_scale(&mut self, scale: &Scale3D) {
- self.m11 *= scale.0;
- self.m12 *= scale.0;
- self.m13 *= scale.0;
- self.m14 *= scale.0;
- self.m21 *= scale.1;
- self.m22 *= scale.1;
- self.m23 *= scale.1;
- self.m24 *= scale.1;
- self.m31 *= scale.2;
- self.m32 *= scale.2;
- self.m33 *= scale.2;
- self.m34 *= scale.2;
- }
-}
-
-#[cfg_attr(rustfmt, rustfmt_skip)]
-impl Matrix {
- #[inline]
- /// Get an identity matrix
- pub fn identity() -> Self {
- Self {
- a: 1., c: 0., /* 0 0*/
- b: 0., d: 1., /* 0 0*/
- /* 0 0 1 0 */
- e: 0., f: 0., /* 0 1 */
- }
- }
-}
-
-#[cfg_attr(rustfmt, rustfmt_skip)]
-impl From<Matrix> for Matrix3D {
- fn from(m: Matrix) -> Self {
- Self {
- m11: m.a, m12: m.b, m13: 0.0, m14: 0.0,
- m21: m.c, m22: m.d, m23: 0.0, m24: 0.0,
- m31: 0.0, m32: 0.0, m33: 1.0, m34: 0.0,
- m41: m.e, m42: m.f, m43: 0.0, m44: 1.0
- }
- }
-}
-
-#[cfg_attr(rustfmt, rustfmt_skip)]
-impl From<Transform3D<CSSFloat>> for Matrix3D {
- #[inline]
- fn from(m: Transform3D<CSSFloat>) -> Self {
- Matrix3D {
- m11: m.m11, m12: m.m12, m13: m.m13, m14: m.m14,
- m21: m.m21, m22: m.m22, m23: m.m23, m24: m.m24,
- m31: m.m31, m32: m.m32, m33: m.m33, m34: m.m34,
- m41: m.m41, m42: m.m42, m43: m.m43, m44: m.m44
- }
- }
-}
-
-impl TransformOperation {
- /// Convert to a Translate3D.
- ///
- /// Must be called on a Translate function
- pub fn to_translate_3d(&self) -> Self {
- match *self {
- generic::TransformOperation::Translate3D(..) => self.clone(),
- generic::TransformOperation::TranslateX(ref x) => {
- generic::TransformOperation::Translate3D(
- x.clone(),
- LengthPercentage::zero(),
- Length::zero(),
- )
- },
- generic::TransformOperation::Translate(ref x, ref y) => {
- generic::TransformOperation::Translate3D(x.clone(), y.clone(), Length::zero())
- },
- generic::TransformOperation::TranslateY(ref y) => {
- generic::TransformOperation::Translate3D(
- LengthPercentage::zero(),
- y.clone(),
- Length::zero(),
- )
- },
- generic::TransformOperation::TranslateZ(ref z) => {
- generic::TransformOperation::Translate3D(
- LengthPercentage::zero(),
- LengthPercentage::zero(),
- z.clone(),
- )
- },
- _ => unreachable!(),
- }
- }
-
- /// Convert to a Rotate3D.
- ///
- /// Must be called on a Rotate function.
- pub fn to_rotate_3d(&self) -> Self {
- match *self {
- generic::TransformOperation::Rotate3D(..) => self.clone(),
- generic::TransformOperation::RotateZ(ref angle) |
- generic::TransformOperation::Rotate(ref angle) => {
- generic::TransformOperation::Rotate3D(0., 0., 1., angle.clone())
- },
- generic::TransformOperation::RotateX(ref angle) => {
- generic::TransformOperation::Rotate3D(1., 0., 0., angle.clone())
- },
- generic::TransformOperation::RotateY(ref angle) => {
- generic::TransformOperation::Rotate3D(0., 1., 0., angle.clone())
- },
- _ => unreachable!(),
- }
- }
-
- /// Convert to a Scale3D.
- ///
- /// Must be called on a Scale function
- pub fn to_scale_3d(&self) -> Self {
- match *self {
- generic::TransformOperation::Scale3D(..) => self.clone(),
- generic::TransformOperation::Scale(x, y) => {
- generic::TransformOperation::Scale3D(x, y, 1.)
- },
- generic::TransformOperation::ScaleX(x) => {
- generic::TransformOperation::Scale3D(x, 1., 1.)
- },
- generic::TransformOperation::ScaleY(y) => {
- generic::TransformOperation::Scale3D(1., y, 1.)
- },
- generic::TransformOperation::ScaleZ(z) => {
- generic::TransformOperation::Scale3D(1., 1., z)
- },
- _ => unreachable!(),
- }
- }
-}
-
-/// Build an equivalent 'identity transform function list' based
-/// on an existing transform list.
-/// http://dev.w3.org/csswg/css-transforms/#none-transform-animation
-impl ToAnimatedZero for TransformOperation {
- fn to_animated_zero(&self) -> Result<Self, ()> {
- match *self {
- generic::TransformOperation::Matrix3D(..) => {
- Ok(generic::TransformOperation::Matrix3D(Matrix3D::identity()))
- },
- generic::TransformOperation::Matrix(..) => {
- Ok(generic::TransformOperation::Matrix(Matrix::identity()))
- },
- generic::TransformOperation::Skew(sx, sy) => Ok(generic::TransformOperation::Skew(
- sx.to_animated_zero()?,
- sy.to_animated_zero()?,
- )),
- generic::TransformOperation::SkewX(s) => {
- Ok(generic::TransformOperation::SkewX(s.to_animated_zero()?))
- },
- generic::TransformOperation::SkewY(s) => {
- Ok(generic::TransformOperation::SkewY(s.to_animated_zero()?))
- },
- generic::TransformOperation::Translate3D(ref tx, ref ty, ref tz) => {
- Ok(generic::TransformOperation::Translate3D(
- tx.to_animated_zero()?,
- ty.to_animated_zero()?,
- tz.to_animated_zero()?,
- ))
- },
- generic::TransformOperation::Translate(ref tx, ref ty) => {
- Ok(generic::TransformOperation::Translate(
- tx.to_animated_zero()?,
- ty.to_animated_zero()?,
- ))
- },
- generic::TransformOperation::TranslateX(ref t) => Ok(
- generic::TransformOperation::TranslateX(t.to_animated_zero()?),
- ),
- generic::TransformOperation::TranslateY(ref t) => Ok(
- generic::TransformOperation::TranslateY(t.to_animated_zero()?),
- ),
- generic::TransformOperation::TranslateZ(ref t) => Ok(
- generic::TransformOperation::TranslateZ(t.to_animated_zero()?),
- ),
- generic::TransformOperation::Scale3D(..) => {
- Ok(generic::TransformOperation::Scale3D(1.0, 1.0, 1.0))
- },
- generic::TransformOperation::Scale(_, _) => {
- Ok(generic::TransformOperation::Scale(1.0, 1.0))
- },
- generic::TransformOperation::ScaleX(..) => Ok(generic::TransformOperation::ScaleX(1.0)),
- generic::TransformOperation::ScaleY(..) => Ok(generic::TransformOperation::ScaleY(1.0)),
- generic::TransformOperation::ScaleZ(..) => Ok(generic::TransformOperation::ScaleZ(1.0)),
- generic::TransformOperation::Rotate3D(x, y, z, a) => {
- let (x, y, z, _) = generic::get_normalized_vector_and_angle(x, y, z, a);
- Ok(generic::TransformOperation::Rotate3D(
- x,
- y,
- z,
- Angle::zero(),
- ))
- },
- generic::TransformOperation::RotateX(_) => {
- Ok(generic::TransformOperation::RotateX(Angle::zero()))
- },
- generic::TransformOperation::RotateY(_) => {
- Ok(generic::TransformOperation::RotateY(Angle::zero()))
- },
- generic::TransformOperation::RotateZ(_) => {
- Ok(generic::TransformOperation::RotateZ(Angle::zero()))
- },
- generic::TransformOperation::Rotate(_) => {
- Ok(generic::TransformOperation::Rotate(Angle::zero()))
- },
- generic::TransformOperation::Perspective(_) => Ok(
- generic::TransformOperation::Perspective(generic::PerspectiveFunction::None),
- ),
- generic::TransformOperation::AccumulateMatrix { .. } |
- generic::TransformOperation::InterpolateMatrix { .. } => {
- // AccumulateMatrix/InterpolateMatrix: We do interpolation on
- // AccumulateMatrix/InterpolateMatrix by reading it as a ComputedMatrix
- // (with layout information), and then do matrix interpolation.
- //
- // Therefore, we use an identity matrix to represent the identity transform list.
- // http://dev.w3.org/csswg/css-transforms/#identity-transform-function
- Ok(generic::TransformOperation::Matrix3D(Matrix3D::identity()))
- },
- }
- }
-}
-
-impl ToAnimatedZero for Transform {
- #[inline]
- fn to_animated_zero(&self) -> Result<Self, ()> {
- Ok(generic::Transform(
- self.0
- .iter()
- .map(|op| op.to_animated_zero())
- .collect::<Result<crate::OwnedSlice<_>, _>>()?,
- ))
- }
-}
-
-/// A computed CSS `rotate`
-pub type Rotate = generic::GenericRotate<Number, Angle>;
-
-/// A computed CSS `translate`
-pub type Translate = generic::GenericTranslate<LengthPercentage, Length>;
-
-/// A computed CSS `scale`
-pub type Scale = generic::GenericScale<Number>;
diff --git a/components/style/values/computed/ui.rs b/components/style/values/computed/ui.rs
deleted file mode 100644
index 6fa5137adf0..00000000000
--- a/components/style/values/computed/ui.rs
+++ /dev/null
@@ -1,22 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
-
-//! Computed values for UI properties
-
-use crate::values::computed::color::Color;
-use crate::values::computed::image::Image;
-use crate::values::computed::Number;
-use crate::values::generics::ui as generics;
-
-pub use crate::values::specified::ui::CursorKind;
-pub use crate::values::specified::ui::{BoolInteger, UserSelect};
-
-/// A computed value for the `cursor` property.
-pub type Cursor = generics::GenericCursor<CursorImage>;
-
-/// A computed value for item of `image cursors`.
-pub type CursorImage = generics::GenericCursorImage<Image, Number>;
-
-/// A computed value for `scrollbar-color` property.
-pub type ScrollbarColor = generics::GenericScrollbarColor<Color>;
diff --git a/components/style/values/computed/url.rs b/components/style/values/computed/url.rs
deleted file mode 100644
index 9f0d8f5bb3e..00000000000
--- a/components/style/values/computed/url.rs
+++ /dev/null
@@ -1,15 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
-
-//! Common handling for the computed value CSS url() values.
-
-use crate::values::generics::url::UrlOrNone as GenericUrlOrNone;
-
-#[cfg(feature = "gecko")]
-pub use crate::gecko::url::{ComputedImageUrl, ComputedUrl};
-#[cfg(feature = "servo")]
-pub use crate::servo::url::{ComputedImageUrl, ComputedUrl};
-
-/// Computed <url> | <none>
-pub type UrlOrNone = GenericUrlOrNone<ComputedUrl>;
diff --git a/components/style/values/distance.rs b/components/style/values/distance.rs
deleted file mode 100644
index fef376cf5f7..00000000000
--- a/components/style/values/distance.rs
+++ /dev/null
@@ -1,138 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
-
-//! Machinery to compute distances between animatable values.
-
-use app_units::Au;
-use euclid::default::Size2D;
-use std::iter::Sum;
-use std::ops::Add;
-
-/// A trait to compute squared distances between two animatable values.
-///
-/// This trait is derivable with `#[derive(ComputeSquaredDistance)]`. The derived
-/// implementation uses a `match` expression with identical patterns for both
-/// `self` and `other`, calling `ComputeSquaredDistance::compute_squared_distance`
-/// on each fields of the values.
-///
-/// If a variant is annotated with `#[animation(error)]`, the corresponding
-/// `match` arm returns an error.
-///
-/// Trait bounds for type parameter `Foo` can be opted out of with
-/// `#[animation(no_bound(Foo))]` on the type definition, trait bounds for
-/// fields can be opted into with `#[distance(field_bound)]` on the field.
-pub trait ComputeSquaredDistance {
- /// Computes the squared distance between two animatable values.
- fn compute_squared_distance(&self, other: &Self) -> Result<SquaredDistance, ()>;
-}
-
-/// A distance between two animatable values.
-#[derive(Add, Clone, Copy, Debug, From)]
-pub struct SquaredDistance {
- value: f64,
-}
-
-impl SquaredDistance {
- /// Returns a squared distance from its square root.
- #[inline]
- pub fn from_sqrt(sqrt: f64) -> Self {
- Self { value: sqrt * sqrt }
- }
-}
-
-impl ComputeSquaredDistance for u16 {
- #[inline]
- fn compute_squared_distance(&self, other: &Self) -> Result<SquaredDistance, ()> {
- Ok(SquaredDistance::from_sqrt(
- ((*self as f64) - (*other as f64)).abs(),
- ))
- }
-}
-
-impl ComputeSquaredDistance for i16 {
- #[inline]
- fn compute_squared_distance(&self, other: &Self) -> Result<SquaredDistance, ()> {
- Ok(SquaredDistance::from_sqrt((*self - *other).abs() as f64))
- }
-}
-
-impl ComputeSquaredDistance for i32 {
- #[inline]
- fn compute_squared_distance(&self, other: &Self) -> Result<SquaredDistance, ()> {
- Ok(SquaredDistance::from_sqrt((*self - *other).abs() as f64))
- }
-}
-
-impl ComputeSquaredDistance for f32 {
- #[inline]
- fn compute_squared_distance(&self, other: &Self) -> Result<SquaredDistance, ()> {
- Ok(SquaredDistance::from_sqrt((*self - *other).abs() as f64))
- }
-}
-
-impl ComputeSquaredDistance for f64 {
- #[inline]
- fn compute_squared_distance(&self, other: &Self) -> Result<SquaredDistance, ()> {
- Ok(SquaredDistance::from_sqrt((*self - *other).abs()))
- }
-}
-
-impl ComputeSquaredDistance for Au {
- #[inline]
- fn compute_squared_distance(&self, other: &Self) -> Result<SquaredDistance, ()> {
- self.0.compute_squared_distance(&other.0)
- }
-}
-
-impl<T> ComputeSquaredDistance for Box<T>
-where
- T: ComputeSquaredDistance,
-{
- #[inline]
- fn compute_squared_distance(&self, other: &Self) -> Result<SquaredDistance, ()> {
- (**self).compute_squared_distance(&**other)
- }
-}
-
-impl<T> ComputeSquaredDistance for Option<T>
-where
- T: ComputeSquaredDistance,
-{
- #[inline]
- fn compute_squared_distance(&self, other: &Self) -> Result<SquaredDistance, ()> {
- match (self.as_ref(), other.as_ref()) {
- (Some(this), Some(other)) => this.compute_squared_distance(other),
- (None, None) => Ok(SquaredDistance::from_sqrt(0.)),
- _ => Err(()),
- }
- }
-}
-
-impl<T> ComputeSquaredDistance for Size2D<T>
-where
- T: ComputeSquaredDistance,
-{
- #[inline]
- fn compute_squared_distance(&self, other: &Self) -> Result<SquaredDistance, ()> {
- Ok(self.width.compute_squared_distance(&other.width)? +
- self.height.compute_squared_distance(&other.height)?)
- }
-}
-
-impl SquaredDistance {
- /// Returns the square root of this squared distance.
- #[inline]
- pub fn sqrt(self) -> f64 {
- self.value.sqrt()
- }
-}
-
-impl Sum for SquaredDistance {
- fn sum<I>(iter: I) -> Self
- where
- I: Iterator<Item = Self>,
- {
- iter.fold(SquaredDistance::from_sqrt(0.), Add::add)
- }
-}
diff --git a/components/style/values/generics/animation.rs b/components/style/values/generics/animation.rs
deleted file mode 100644
index edee9e9f257..00000000000
--- a/components/style/values/generics/animation.rs
+++ /dev/null
@@ -1,140 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
-
-//! Generic values for properties related to animations and transitions.
-
-use crate::values::generics::length::GenericLengthPercentageOrAuto;
-use crate::values::specified::animation::{ScrollAxis, ScrollFunction};
-use crate::values::TimelineName;
-use std::fmt::{self, Write};
-use style_traits::{CssWriter, ToCss};
-
-/// The view() notation.
-/// https://drafts.csswg.org/scroll-animations-1/#view-notation
-#[derive(
- Clone,
- Debug,
- MallocSizeOf,
- PartialEq,
- SpecifiedValueInfo,
- ToComputedValue,
- ToCss,
- ToResolvedValue,
- ToShmem,
-)]
-#[css(function = "view")]
-#[repr(C)]
-pub struct GenericViewFunction<LengthPercent> {
- /// The axis of scrolling that drives the progress of the timeline.
- #[css(skip_if = "ScrollAxis::is_default")]
- pub axis: ScrollAxis,
- /// An adjustment of the view progress visibility range.
- #[css(skip_if = "GenericViewTimelineInset::is_auto")]
- #[css(field_bound)]
- pub inset: GenericViewTimelineInset<LengthPercent>,
-}
-
-pub use self::GenericViewFunction as ViewFunction;
-
-/// A value for the <single-animation-timeline>.
-///
-/// https://drafts.csswg.org/css-animations-2/#typedef-single-animation-timeline
-#[derive(
- Clone,
- Debug,
- MallocSizeOf,
- PartialEq,
- SpecifiedValueInfo,
- ToComputedValue,
- ToCss,
- ToResolvedValue,
- ToShmem,
-)]
-#[repr(C, u8)]
-pub enum GenericAnimationTimeline<LengthPercent> {
- /// Use default timeline. The animation’s timeline is a DocumentTimeline.
- Auto,
- /// The scroll-timeline name or view-timeline-name.
- /// https://drafts.csswg.org/scroll-animations-1/#scroll-timelines-named
- /// https://drafts.csswg.org/scroll-animations-1/#view-timeline-name
- Timeline(TimelineName),
- /// The scroll() notation.
- /// https://drafts.csswg.org/scroll-animations-1/#scroll-notation
- Scroll(ScrollFunction),
- /// The view() notation.
- /// https://drafts.csswg.org/scroll-animations-1/#view-notation
- View(#[css(field_bound)] GenericViewFunction<LengthPercent>),
-}
-
-pub use self::GenericAnimationTimeline as AnimationTimeline;
-
-impl<LengthPercent> AnimationTimeline<LengthPercent> {
- /// Returns the `auto` value.
- pub fn auto() -> Self {
- Self::Auto
- }
-
- /// Returns true if it is auto (i.e. the default value).
- pub fn is_auto(&self) -> bool {
- matches!(self, Self::Auto)
- }
-}
-
-/// A generic value for the `[ [ auto | <length-percentage> ]{1,2} ]`.
-///
-/// https://drafts.csswg.org/scroll-animations-1/#view-timeline-inset
-#[derive(
- Clone,
- Copy,
- Debug,
- MallocSizeOf,
- PartialEq,
- SpecifiedValueInfo,
- ToComputedValue,
- ToResolvedValue,
- ToShmem,
-)]
-#[repr(C)]
-pub struct GenericViewTimelineInset<LengthPercent> {
- /// The start inset in the relevant axis.
- pub start: GenericLengthPercentageOrAuto<LengthPercent>,
- /// The end inset.
- pub end: GenericLengthPercentageOrAuto<LengthPercent>,
-}
-
-pub use self::GenericViewTimelineInset as ViewTimelineInset;
-
-impl<LengthPercent> ViewTimelineInset<LengthPercent> {
- /// Returns true if it is auto.
- #[inline]
- fn is_auto(&self) -> bool {
- self.start.is_auto() && self.end.is_auto()
- }
-}
-
-impl<LengthPercent> ToCss for ViewTimelineInset<LengthPercent>
-where
- LengthPercent: PartialEq + ToCss,
-{
- fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
- where
- W: Write,
- {
- self.start.to_css(dest)?;
- if self.end != self.start {
- dest.write_char(' ')?;
- self.end.to_css(dest)?;
- }
- Ok(())
- }
-}
-
-impl<LengthPercent> Default for ViewTimelineInset<LengthPercent> {
- fn default() -> Self {
- Self {
- start: GenericLengthPercentageOrAuto::auto(),
- end: GenericLengthPercentageOrAuto::auto(),
- }
- }
-}
diff --git a/components/style/values/generics/background.rs b/components/style/values/generics/background.rs
deleted file mode 100644
index d9b6624595d..00000000000
--- a/components/style/values/generics/background.rs
+++ /dev/null
@@ -1,54 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
-
-//! Generic types for CSS values related to backgrounds.
-
-use crate::values::generics::length::{GenericLengthPercentageOrAuto, LengthPercentageOrAuto};
-
-/// A generic value for the `background-size` property.
-#[derive(
- Animate,
- Clone,
- ComputeSquaredDistance,
- Copy,
- Debug,
- MallocSizeOf,
- PartialEq,
- SpecifiedValueInfo,
- ToAnimatedValue,
- ToAnimatedZero,
- ToComputedValue,
- ToCss,
- ToResolvedValue,
- ToShmem,
-)]
-#[repr(C, u8)]
-pub enum GenericBackgroundSize<LengthPercent> {
- /// `<width> <height>`
- ExplicitSize {
- /// Explicit width.
- width: GenericLengthPercentageOrAuto<LengthPercent>,
- /// Explicit height.
- #[css(skip_if = "GenericLengthPercentageOrAuto::is_auto")]
- height: GenericLengthPercentageOrAuto<LengthPercent>,
- },
- /// `cover`
- #[animation(error)]
- Cover,
- /// `contain`
- #[animation(error)]
- Contain,
-}
-
-pub use self::GenericBackgroundSize as BackgroundSize;
-
-impl<LengthPercentage> BackgroundSize<LengthPercentage> {
- /// Returns `auto auto`.
- pub fn auto() -> Self {
- GenericBackgroundSize::ExplicitSize {
- width: LengthPercentageOrAuto::Auto,
- height: LengthPercentageOrAuto::Auto,
- }
- }
-}
diff --git a/components/style/values/generics/basic_shape.rs b/components/style/values/generics/basic_shape.rs
deleted file mode 100644
index 00d682a169f..00000000000
--- a/components/style/values/generics/basic_shape.rs
+++ /dev/null
@@ -1,513 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
-
-//! CSS handling for the [`basic-shape`](https://drafts.csswg.org/css-shapes/#typedef-basic-shape)
-//! types that are generic over their `ToCss` implementations.
-
-use crate::values::animated::{lists, Animate, Procedure, ToAnimatedZero};
-use crate::values::distance::{ComputeSquaredDistance, SquaredDistance};
-use crate::values::generics::border::GenericBorderRadius;
-use crate::values::generics::position::GenericPosition;
-use crate::values::generics::rect::Rect;
-use crate::values::specified::SVGPathData;
-use crate::Zero;
-use std::fmt::{self, Write};
-use style_traits::{CssWriter, ToCss};
-
-/// <https://drafts.fxtf.org/css-masking-1/#typedef-geometry-box>
-#[allow(missing_docs)]
-#[derive(
- Animate,
- Clone,
- ComputeSquaredDistance,
- Copy,
- Debug,
- MallocSizeOf,
- PartialEq,
- Parse,
- SpecifiedValueInfo,
- ToAnimatedValue,
- ToComputedValue,
- ToCss,
- ToResolvedValue,
- ToShmem,
-)]
-#[repr(u8)]
-pub enum ShapeGeometryBox {
- /// Depending on which kind of element this style value applied on, the
- /// default value of the reference-box can be different. For an HTML
- /// element, the default value of reference-box is border-box; for an SVG
- /// element, the default value is fill-box. Since we can not determine the
- /// default value at parsing time, we keep this value to make a decision on
- /// it.
- #[css(skip)]
- ElementDependent,
- FillBox,
- StrokeBox,
- ViewBox,
- ShapeBox(ShapeBox),
-}
-
-impl Default for ShapeGeometryBox {
- fn default() -> Self {
- Self::ElementDependent
- }
-}
-
-/// https://drafts.csswg.org/css-shapes-1/#typedef-shape-box
-#[allow(missing_docs)]
-#[cfg_attr(feature = "servo", derive(Deserialize, Serialize))]
-#[derive(
- Animate,
- Clone,
- Copy,
- ComputeSquaredDistance,
- Debug,
- Eq,
- MallocSizeOf,
- Parse,
- PartialEq,
- SpecifiedValueInfo,
- ToAnimatedValue,
- ToComputedValue,
- ToCss,
- ToResolvedValue,
- ToShmem,
-)]
-#[repr(u8)]
-pub enum ShapeBox {
- MarginBox,
- BorderBox,
- PaddingBox,
- ContentBox,
-}
-
-impl Default for ShapeBox {
- fn default() -> Self {
- ShapeBox::MarginBox
- }
-}
-
-/// A value for the `clip-path` property.
-#[allow(missing_docs)]
-#[derive(
- Animate,
- Clone,
- ComputeSquaredDistance,
- Debug,
- MallocSizeOf,
- PartialEq,
- SpecifiedValueInfo,
- ToAnimatedValue,
- ToComputedValue,
- ToCss,
- ToResolvedValue,
- ToShmem,
-)]
-#[animation(no_bound(U))]
-#[repr(u8)]
-pub enum GenericClipPath<BasicShape, U> {
- #[animation(error)]
- None,
- #[animation(error)]
- Url(U),
- #[css(function)]
- Path(Path),
- Shape(
- Box<BasicShape>,
- #[css(skip_if = "is_default")] ShapeGeometryBox,
- ),
- #[animation(error)]
- Box(ShapeGeometryBox),
-}
-
-pub use self::GenericClipPath as ClipPath;
-
-/// A value for the `shape-outside` property.
-#[allow(missing_docs)]
-#[derive(
- Animate,
- Clone,
- ComputeSquaredDistance,
- Debug,
- MallocSizeOf,
- PartialEq,
- SpecifiedValueInfo,
- ToAnimatedValue,
- ToComputedValue,
- ToCss,
- ToResolvedValue,
- ToShmem,
-)]
-#[animation(no_bound(I))]
-#[repr(u8)]
-pub enum GenericShapeOutside<BasicShape, I> {
- #[animation(error)]
- None,
- #[animation(error)]
- Image(I),
- Shape(Box<BasicShape>, #[css(skip_if = "is_default")] ShapeBox),
- #[animation(error)]
- Box(ShapeBox),
-}
-
-pub use self::GenericShapeOutside as ShapeOutside;
-
-#[allow(missing_docs)]
-#[derive(
- Animate,
- Clone,
- ComputeSquaredDistance,
- Debug,
- MallocSizeOf,
- PartialEq,
- SpecifiedValueInfo,
- ToAnimatedValue,
- ToComputedValue,
- ToCss,
- ToResolvedValue,
- ToShmem,
-)]
-#[repr(C, u8)]
-pub enum GenericBasicShape<H, V, LengthPercentage, NonNegativeLengthPercentage> {
- Inset(
- #[css(field_bound)]
- #[shmem(field_bound)]
- InsetRect<LengthPercentage, NonNegativeLengthPercentage>,
- ),
- Circle(
- #[css(field_bound)]
- #[shmem(field_bound)]
- Circle<H, V, NonNegativeLengthPercentage>,
- ),
- Ellipse(
- #[css(field_bound)]
- #[shmem(field_bound)]
- Ellipse<H, V, NonNegativeLengthPercentage>,
- ),
- Polygon(GenericPolygon<LengthPercentage>),
-}
-
-pub use self::GenericBasicShape as BasicShape;
-
-/// <https://drafts.csswg.org/css-shapes/#funcdef-inset>
-#[allow(missing_docs)]
-#[derive(
- Animate,
- Clone,
- ComputeSquaredDistance,
- Debug,
- MallocSizeOf,
- PartialEq,
- SpecifiedValueInfo,
- ToAnimatedValue,
- ToComputedValue,
- ToResolvedValue,
- ToShmem,
-)]
-#[css(function = "inset")]
-#[repr(C)]
-pub struct InsetRect<LengthPercentage, NonNegativeLengthPercentage> {
- pub rect: Rect<LengthPercentage>,
- #[shmem(field_bound)]
- pub round: GenericBorderRadius<NonNegativeLengthPercentage>,
-}
-
-/// <https://drafts.csswg.org/css-shapes/#funcdef-circle>
-#[allow(missing_docs)]
-#[derive(
- Animate,
- Clone,
- ComputeSquaredDistance,
- Copy,
- Debug,
- MallocSizeOf,
- PartialEq,
- SpecifiedValueInfo,
- ToAnimatedValue,
- ToComputedValue,
- ToResolvedValue,
- ToShmem,
-)]
-#[css(function)]
-#[repr(C)]
-pub struct Circle<H, V, NonNegativeLengthPercentage> {
- pub position: GenericPosition<H, V>,
- pub radius: GenericShapeRadius<NonNegativeLengthPercentage>,
-}
-
-/// <https://drafts.csswg.org/css-shapes/#funcdef-ellipse>
-#[allow(missing_docs)]
-#[derive(
- Animate,
- Clone,
- ComputeSquaredDistance,
- Copy,
- Debug,
- MallocSizeOf,
- PartialEq,
- SpecifiedValueInfo,
- ToAnimatedValue,
- ToComputedValue,
- ToResolvedValue,
- ToShmem,
-)]
-#[css(function)]
-#[repr(C)]
-pub struct Ellipse<H, V, NonNegativeLengthPercentage> {
- pub position: GenericPosition<H, V>,
- pub semiaxis_x: GenericShapeRadius<NonNegativeLengthPercentage>,
- pub semiaxis_y: GenericShapeRadius<NonNegativeLengthPercentage>,
-}
-
-/// <https://drafts.csswg.org/css-shapes/#typedef-shape-radius>
-#[allow(missing_docs)]
-#[derive(
- Animate,
- Clone,
- ComputeSquaredDistance,
- Copy,
- Debug,
- MallocSizeOf,
- Parse,
- PartialEq,
- SpecifiedValueInfo,
- ToAnimatedValue,
- ToComputedValue,
- ToCss,
- ToResolvedValue,
- ToShmem,
-)]
-#[repr(C, u8)]
-pub enum GenericShapeRadius<NonNegativeLengthPercentage> {
- Length(NonNegativeLengthPercentage),
- #[animation(error)]
- ClosestSide,
- #[animation(error)]
- FarthestSide,
-}
-
-pub use self::GenericShapeRadius as ShapeRadius;
-
-/// A generic type for representing the `polygon()` function
-///
-/// <https://drafts.csswg.org/css-shapes/#funcdef-polygon>
-#[derive(
- Clone,
- Debug,
- MallocSizeOf,
- PartialEq,
- SpecifiedValueInfo,
- ToAnimatedValue,
- ToComputedValue,
- ToCss,
- ToResolvedValue,
- ToShmem,
-)]
-#[css(comma, function = "polygon")]
-#[repr(C)]
-pub struct GenericPolygon<LengthPercentage> {
- /// The filling rule for a polygon.
- #[css(skip_if = "is_default")]
- pub fill: FillRule,
- /// A collection of (x, y) coordinates to draw the polygon.
- #[css(iterable)]
- pub coordinates: crate::OwnedSlice<PolygonCoord<LengthPercentage>>,
-}
-
-pub use self::GenericPolygon as Polygon;
-
-/// Coordinates for Polygon.
-#[derive(
- Animate,
- Clone,
- ComputeSquaredDistance,
- Debug,
- MallocSizeOf,
- PartialEq,
- SpecifiedValueInfo,
- ToAnimatedValue,
- ToComputedValue,
- ToCss,
- ToResolvedValue,
- ToShmem,
-)]
-#[repr(C)]
-pub struct PolygonCoord<LengthPercentage>(pub LengthPercentage, pub LengthPercentage);
-
-// https://drafts.csswg.org/css-shapes/#typedef-fill-rule
-// NOTE: Basic shapes spec says that these are the only two values, however
-// https://www.w3.org/TR/SVG/painting.html#FillRuleProperty
-// says that it can also be `inherit`
-#[allow(missing_docs)]
-#[cfg_attr(feature = "servo", derive(Deserialize, Serialize))]
-#[derive(
- Clone,
- Copy,
- Debug,
- Eq,
- MallocSizeOf,
- Parse,
- PartialEq,
- SpecifiedValueInfo,
- ToAnimatedValue,
- ToComputedValue,
- ToCss,
- ToResolvedValue,
- ToShmem,
-)]
-#[repr(u8)]
-pub enum FillRule {
- Nonzero,
- Evenodd,
-}
-
-/// The path function defined in css-shape-2.
-///
-/// https://drafts.csswg.org/css-shapes-2/#funcdef-path
-#[derive(
- Animate,
- Clone,
- ComputeSquaredDistance,
- Debug,
- MallocSizeOf,
- PartialEq,
- SpecifiedValueInfo,
- ToAnimatedValue,
- ToComputedValue,
- ToCss,
- ToResolvedValue,
- ToShmem,
-)]
-#[css(comma)]
-#[repr(C)]
-pub struct Path {
- /// The filling rule for the svg path.
- #[css(skip_if = "is_default")]
- #[animation(constant)]
- pub fill: FillRule,
- /// The svg path data.
- pub path: SVGPathData,
-}
-
-impl<B, U> ToAnimatedZero for ClipPath<B, U> {
- fn to_animated_zero(&self) -> Result<Self, ()> {
- Err(())
- }
-}
-
-impl<B, U> ToAnimatedZero for ShapeOutside<B, U> {
- fn to_animated_zero(&self) -> Result<Self, ()> {
- Err(())
- }
-}
-
-impl<Length, NonNegativeLength> ToCss for InsetRect<Length, NonNegativeLength>
-where
- Length: ToCss + PartialEq,
- NonNegativeLength: ToCss + PartialEq + Zero,
-{
- fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
- where
- W: Write,
- {
- dest.write_str("inset(")?;
- self.rect.to_css(dest)?;
- if !self.round.is_zero() {
- dest.write_str(" round ")?;
- self.round.to_css(dest)?;
- }
- dest.write_char(')')
- }
-}
-
-impl<H, V, NonNegativeLengthPercentage> ToCss for Circle<H, V, NonNegativeLengthPercentage>
-where
- GenericPosition<H, V>: ToCss,
- NonNegativeLengthPercentage: ToCss + PartialEq,
-{
- fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
- where
- W: Write,
- {
- dest.write_str("circle(")?;
- if self.radius != Default::default() {
- self.radius.to_css(dest)?;
- dest.write_char(' ')?;
- }
- dest.write_str("at ")?;
- self.position.to_css(dest)?;
- dest.write_char(')')
- }
-}
-
-impl<H, V, NonNegativeLengthPercentage> ToCss for Ellipse<H, V, NonNegativeLengthPercentage>
-where
- GenericPosition<H, V>: ToCss,
- NonNegativeLengthPercentage: ToCss + PartialEq,
-{
- fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
- where
- W: Write,
- {
- dest.write_str("ellipse(")?;
- if self.semiaxis_x != Default::default() || self.semiaxis_y != Default::default() {
- self.semiaxis_x.to_css(dest)?;
- dest.write_char(' ')?;
- self.semiaxis_y.to_css(dest)?;
- dest.write_char(' ')?;
- }
- dest.write_str("at ")?;
- self.position.to_css(dest)?;
- dest.write_char(')')
- }
-}
-
-impl<L> Default for ShapeRadius<L> {
- #[inline]
- fn default() -> Self {
- ShapeRadius::ClosestSide
- }
-}
-
-impl<L> Animate for Polygon<L>
-where
- L: Animate,
-{
- fn animate(&self, other: &Self, procedure: Procedure) -> Result<Self, ()> {
- if self.fill != other.fill {
- return Err(());
- }
- let coordinates =
- lists::by_computed_value::animate(&self.coordinates, &other.coordinates, procedure)?;
- Ok(Polygon {
- fill: self.fill,
- coordinates,
- })
- }
-}
-
-impl<L> ComputeSquaredDistance for Polygon<L>
-where
- L: ComputeSquaredDistance,
-{
- fn compute_squared_distance(&self, other: &Self) -> Result<SquaredDistance, ()> {
- if self.fill != other.fill {
- return Err(());
- }
- lists::by_computed_value::squared_distance(&self.coordinates, &other.coordinates)
- }
-}
-
-impl Default for FillRule {
- #[inline]
- fn default() -> Self {
- FillRule::Nonzero
- }
-}
-
-#[inline]
-fn is_default<T: Default + PartialEq>(fill: &T) -> bool {
- *fill == Default::default()
-}
diff --git a/components/style/values/generics/border.rs b/components/style/values/generics/border.rs
deleted file mode 100644
index e4e78e31222..00000000000
--- a/components/style/values/generics/border.rs
+++ /dev/null
@@ -1,257 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
-
-//! Generic types for CSS values related to borders.
-
-use crate::values::generics::rect::Rect;
-use crate::values::generics::size::Size2D;
-use crate::Zero;
-use std::fmt::{self, Write};
-use style_traits::{CssWriter, ToCss};
-
-/// A generic value for a single side of a `border-image-width` property.
-#[derive(
- Animate,
- Clone,
- ComputeSquaredDistance,
- Copy,
- Debug,
- MallocSizeOf,
- Parse,
- PartialEq,
- SpecifiedValueInfo,
- ToAnimatedValue,
- ToAnimatedZero,
- ToComputedValue,
- ToCss,
- ToResolvedValue,
- ToShmem,
-)]
-#[repr(C, u8)]
-pub enum GenericBorderImageSideWidth<LP, N> {
- /// `<number>`
- ///
- /// NOTE: Numbers need to be before length-percentagess, in order to parse
- /// them first, since `0` should be a number, not the `0px` length.
- Number(N),
- /// `<length-or-percentage>`
- LengthPercentage(LP),
- /// `auto`
- Auto,
-}
-
-pub use self::GenericBorderImageSideWidth as BorderImageSideWidth;
-
-/// A generic value for the `border-image-slice` property.
-#[derive(
- Animate,
- Clone,
- ComputeSquaredDistance,
- Copy,
- Debug,
- MallocSizeOf,
- PartialEq,
- SpecifiedValueInfo,
- ToAnimatedValue,
- ToAnimatedZero,
- ToComputedValue,
- ToCss,
- ToResolvedValue,
- ToShmem,
-)]
-#[repr(C)]
-pub struct GenericBorderImageSlice<NumberOrPercentage> {
- /// The offsets.
- #[css(field_bound)]
- pub offsets: Rect<NumberOrPercentage>,
- /// Whether to fill the middle part.
- #[animation(constant)]
- #[css(represents_keyword)]
- pub fill: bool,
-}
-
-pub use self::GenericBorderImageSlice as BorderImageSlice;
-
-/// A generic value for the `border-*-radius` longhand properties.
-#[derive(
- Animate,
- Clone,
- ComputeSquaredDistance,
- Copy,
- Debug,
- MallocSizeOf,
- PartialEq,
- SpecifiedValueInfo,
- ToAnimatedValue,
- ToAnimatedZero,
- ToComputedValue,
- ToCss,
- ToResolvedValue,
- ToShmem,
-)]
-#[repr(C)]
-pub struct GenericBorderCornerRadius<L>(
- #[css(field_bound)]
- #[shmem(field_bound)]
- pub Size2D<L>,
-);
-
-pub use self::GenericBorderCornerRadius as BorderCornerRadius;
-
-impl<L> BorderCornerRadius<L> {
- /// Trivially create a `BorderCornerRadius`.
- pub fn new(w: L, h: L) -> Self {
- BorderCornerRadius(Size2D::new(w, h))
- }
-}
-
-impl<L: Zero> Zero for BorderCornerRadius<L> {
- fn zero() -> Self {
- BorderCornerRadius(Size2D::zero())
- }
-
- fn is_zero(&self) -> bool {
- self.0.is_zero()
- }
-}
-
-/// A generic value for the `border-spacing` property.
-#[derive(
- Animate,
- Clone,
- ComputeSquaredDistance,
- Copy,
- Debug,
- MallocSizeOf,
- PartialEq,
- SpecifiedValueInfo,
- ToAnimatedValue,
- ToAnimatedZero,
- ToComputedValue,
- ToCss,
- ToResolvedValue,
- ToShmem,
-)]
-#[repr(transparent)]
-pub struct BorderSpacing<L>(
- #[css(field_bound)]
- #[shmem(field_bound)]
- pub Size2D<L>,
-);
-
-impl<L> BorderSpacing<L> {
- /// Trivially create a `BorderCornerRadius`.
- pub fn new(w: L, h: L) -> Self {
- BorderSpacing(Size2D::new(w, h))
- }
-}
-
-/// A generic value for `border-radius` and `inset()`.
-///
-/// <https://drafts.csswg.org/css-backgrounds-3/#border-radius>
-#[derive(
- Animate,
- Clone,
- ComputeSquaredDistance,
- Copy,
- Debug,
- MallocSizeOf,
- PartialEq,
- SpecifiedValueInfo,
- ToAnimatedValue,
- ToComputedValue,
- ToResolvedValue,
- ToShmem,
-)]
-#[repr(C)]
-pub struct GenericBorderRadius<LengthPercentage> {
- /// The top left radius.
- #[shmem(field_bound)]
- pub top_left: GenericBorderCornerRadius<LengthPercentage>,
- /// The top right radius.
- pub top_right: GenericBorderCornerRadius<LengthPercentage>,
- /// The bottom right radius.
- pub bottom_right: GenericBorderCornerRadius<LengthPercentage>,
- /// The bottom left radius.
- pub bottom_left: GenericBorderCornerRadius<LengthPercentage>,
-}
-
-pub use self::GenericBorderRadius as BorderRadius;
-
-impl<L> BorderRadius<L> {
- /// Returns a new `BorderRadius<L>`.
- #[inline]
- pub fn new(
- tl: BorderCornerRadius<L>,
- tr: BorderCornerRadius<L>,
- br: BorderCornerRadius<L>,
- bl: BorderCornerRadius<L>,
- ) -> Self {
- BorderRadius {
- top_left: tl,
- top_right: tr,
- bottom_right: br,
- bottom_left: bl,
- }
- }
-
- /// Serialises two given rects following the syntax of the `border-radius``
- /// property.
- pub fn serialize_rects<W>(
- widths: Rect<&L>,
- heights: Rect<&L>,
- dest: &mut CssWriter<W>,
- ) -> fmt::Result
- where
- L: PartialEq + ToCss,
- W: Write,
- {
- widths.to_css(dest)?;
- if widths != heights {
- dest.write_str(" / ")?;
- heights.to_css(dest)?;
- }
- Ok(())
- }
-}
-
-impl<L: Zero> Zero for BorderRadius<L> {
- fn zero() -> Self {
- Self::new(
- BorderCornerRadius::<L>::zero(),
- BorderCornerRadius::<L>::zero(),
- BorderCornerRadius::<L>::zero(),
- BorderCornerRadius::<L>::zero(),
- )
- }
-
- fn is_zero(&self) -> bool {
- self.top_left.is_zero() &&
- self.top_right.is_zero() &&
- self.bottom_right.is_zero() &&
- self.bottom_left.is_zero()
- }
-}
-
-impl<L> ToCss for BorderRadius<L>
-where
- L: PartialEq + ToCss,
-{
- fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
- where
- W: Write,
- {
- let BorderRadius {
- top_left: BorderCornerRadius(ref tl),
- top_right: BorderCornerRadius(ref tr),
- bottom_right: BorderCornerRadius(ref br),
- bottom_left: BorderCornerRadius(ref bl),
- } = *self;
-
- let widths = Rect::new(&tl.width, &tr.width, &br.width, &bl.width);
- let heights = Rect::new(&tl.height, &tr.height, &br.height, &bl.height);
-
- Self::serialize_rects(widths, heights, dest)
- }
-}
diff --git a/components/style/values/generics/box.rs b/components/style/values/generics/box.rs
deleted file mode 100644
index 3cc2b051455..00000000000
--- a/components/style/values/generics/box.rs
+++ /dev/null
@@ -1,208 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
-
-//! Generic types for box properties.
-
-use crate::values::animated::ToAnimatedZero;
-use std::fmt::{self, Write};
-use style_traits::{CssWriter, ToCss};
-
-#[derive(
- Animate,
- Clone,
- ComputeSquaredDistance,
- Copy,
- Debug,
- FromPrimitive,
- MallocSizeOf,
- Parse,
- PartialEq,
- SpecifiedValueInfo,
- ToComputedValue,
- ToCss,
- ToResolvedValue,
- ToShmem,
-)]
-#[repr(u8)]
-#[allow(missing_docs)]
-pub enum VerticalAlignKeyword {
- Baseline,
- Sub,
- Super,
- Top,
- TextTop,
- Middle,
- Bottom,
- TextBottom,
- #[cfg(feature = "gecko")]
- MozMiddleWithBaseline,
-}
-
-/// A generic value for the `vertical-align` property.
-#[derive(
- Animate,
- Clone,
- ComputeSquaredDistance,
- Copy,
- Debug,
- MallocSizeOf,
- PartialEq,
- SpecifiedValueInfo,
- ToComputedValue,
- ToCss,
- ToResolvedValue,
- ToShmem,
-)]
-#[repr(C, u8)]
-pub enum GenericVerticalAlign<LengthPercentage> {
- /// One of the vertical-align keywords.
- Keyword(VerticalAlignKeyword),
- /// `<length-percentage>`
- Length(LengthPercentage),
-}
-
-pub use self::GenericVerticalAlign as VerticalAlign;
-
-impl<L> VerticalAlign<L> {
- /// Returns `baseline`.
- #[inline]
- pub fn baseline() -> Self {
- VerticalAlign::Keyword(VerticalAlignKeyword::Baseline)
- }
-}
-
-impl<L> ToAnimatedZero for VerticalAlign<L> {
- fn to_animated_zero(&self) -> Result<Self, ()> {
- Err(())
- }
-}
-
-/// https://drafts.csswg.org/css-sizing-4/#intrinsic-size-override
-#[derive(
- Animate,
- Clone,
- ComputeSquaredDistance,
- Debug,
- MallocSizeOf,
- PartialEq,
- SpecifiedValueInfo,
- ToComputedValue,
- ToAnimatedValue,
- ToAnimatedZero,
- ToResolvedValue,
- ToShmem,
-)]
-#[value_info(other_values = "auto")]
-#[repr(C, u8)]
-pub enum GenericContainIntrinsicSize<L> {
- /// The keyword `none`.
- None,
- /// A non-negative length.
- Length(L),
- /// "auto <Length>"
- AutoLength(L),
-}
-
-pub use self::GenericContainIntrinsicSize as ContainIntrinsicSize;
-
-impl<L: ToCss> ToCss for ContainIntrinsicSize<L> {
- fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
- where
- W: Write,
- {
- match *self {
- Self::None => dest.write_str("none"),
- Self::Length(ref l) => l.to_css(dest),
- Self::AutoLength(ref l) => {
- dest.write_str("auto ")?;
- l.to_css(dest)
- },
- }
- }
-}
-
-/// Note that we only implement -webkit-line-clamp as a single, longhand
-/// property for now, but the spec defines line-clamp as a shorthand for
-/// separate max-lines, block-ellipsis, and continue properties.
-///
-/// https://drafts.csswg.org/css-overflow-3/#line-clamp
-#[derive(
- Clone,
- ComputeSquaredDistance,
- Copy,
- Debug,
- MallocSizeOf,
- PartialEq,
- SpecifiedValueInfo,
- ToComputedValue,
- ToAnimatedValue,
- ToAnimatedZero,
- ToResolvedValue,
- ToShmem,
-)]
-#[repr(transparent)]
-#[value_info(other_values = "none")]
-pub struct GenericLineClamp<I>(pub I);
-
-pub use self::GenericLineClamp as LineClamp;
-
-impl<I: crate::Zero> LineClamp<I> {
- /// Returns the `none` value.
- pub fn none() -> Self {
- Self(crate::Zero::zero())
- }
-
- /// Returns whether we're the `none` value.
- pub fn is_none(&self) -> bool {
- self.0.is_zero()
- }
-}
-
-impl<I: crate::Zero + ToCss> ToCss for LineClamp<I> {
- fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
- where
- W: Write,
- {
- if self.is_none() {
- return dest.write_str("none");
- }
- self.0.to_css(dest)
- }
-}
-
-/// A generic value for the `perspective` property.
-#[derive(
- Animate,
- Clone,
- ComputeSquaredDistance,
- Copy,
- Debug,
- MallocSizeOf,
- Parse,
- PartialEq,
- SpecifiedValueInfo,
- ToAnimatedValue,
- ToAnimatedZero,
- ToComputedValue,
- ToCss,
- ToResolvedValue,
- ToShmem,
-)]
-#[repr(C, u8)]
-pub enum GenericPerspective<NonNegativeLength> {
- /// A non-negative length.
- Length(NonNegativeLength),
- /// The keyword `none`.
- None,
-}
-
-pub use self::GenericPerspective as Perspective;
-
-impl<L> Perspective<L> {
- /// Returns `none`.
- #[inline]
- pub fn none() -> Self {
- Perspective::None
- }
-}
diff --git a/components/style/values/generics/calc.rs b/components/style/values/generics/calc.rs
deleted file mode 100644
index 3132e56342f..00000000000
--- a/components/style/values/generics/calc.rs
+++ /dev/null
@@ -1,1343 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
-
-//! [Calc expressions][calc].
-//!
-//! [calc]: https://drafts.csswg.org/css-values/#calc-notation
-
-use num_traits::{Float, Zero};
-use smallvec::SmallVec;
-use std::fmt::{self, Write};
-use std::ops::{Add, Div, Mul, Neg, Rem, Sub};
-use std::{cmp, mem};
-use style_traits::{CssWriter, ToCss};
-
-/// Whether we're a `min` or `max` function.
-#[derive(
- Clone,
- Copy,
- Debug,
- Deserialize,
- MallocSizeOf,
- PartialEq,
- Serialize,
- ToAnimatedZero,
- ToResolvedValue,
- ToShmem,
-)]
-#[repr(u8)]
-pub enum MinMaxOp {
- /// `min()`
- Min,
- /// `max()`
- Max,
-}
-
-/// Whether we're a `mod` or `rem` function.
-#[derive(
- Clone,
- Copy,
- Debug,
- Deserialize,
- MallocSizeOf,
- PartialEq,
- Serialize,
- ToAnimatedZero,
- ToResolvedValue,
- ToShmem,
-)]
-#[repr(u8)]
-pub enum ModRemOp {
- /// `mod()`
- Mod,
- /// `rem()`
- Rem,
-}
-
-/// The strategy used in `round()`
-#[derive(
- Clone,
- Copy,
- Debug,
- Deserialize,
- MallocSizeOf,
- PartialEq,
- Serialize,
- ToAnimatedZero,
- ToResolvedValue,
- ToShmem,
-)]
-#[repr(u8)]
-pub enum RoundingStrategy {
- /// `round(nearest, a, b)`
- /// round a to the nearest multiple of b
- Nearest,
- /// `round(up, a, b)`
- /// round a up to the nearest multiple of b
- Up,
- /// `round(down, a, b)`
- /// round a down to the nearest multiple of b
- Down,
- /// `round(to-zero, a, b)`
- /// round a to the nearest multiple of b that is towards zero
- ToZero,
-}
-
-/// This determines the order in which we serialize members of a calc() sum.
-///
-/// See https://drafts.csswg.org/css-values-4/#sort-a-calculations-children
-#[derive(Clone, Copy, Debug, Eq, Ord, PartialEq, PartialOrd)]
-#[allow(missing_docs)]
-pub enum SortKey {
- Number,
- Percentage,
- Cap,
- Ch,
- Cqb,
- Cqh,
- Cqi,
- Cqmax,
- Cqmin,
- Cqw,
- Deg,
- Dppx,
- Dvb,
- Dvh,
- Dvi,
- Dvmax,
- Dvmin,
- Dvw,
- Em,
- Ex,
- Ic,
- Lvb,
- Lvh,
- Lvi,
- Lvmax,
- Lvmin,
- Lvw,
- Px,
- Rem,
- Sec,
- Svb,
- Svh,
- Svi,
- Svmax,
- Svmin,
- Svw,
- Vb,
- Vh,
- Vi,
- Vmax,
- Vmin,
- Vw,
- Other,
-}
-
-/// A generic node in a calc expression.
-///
-/// FIXME: This would be much more elegant if we used `Self` in the types below,
-/// but we can't because of https://github.com/serde-rs/serde/issues/1565.
-///
-/// FIXME: The following annotations are to workaround an LLVM inlining bug, see
-/// bug 1631929.
-///
-/// cbindgen:destructor-attributes=MOZ_NEVER_INLINE
-/// cbindgen:copy-constructor-attributes=MOZ_NEVER_INLINE
-/// cbindgen:eq-attributes=MOZ_NEVER_INLINE
-#[repr(u8)]
-#[derive(
- Clone,
- Debug,
- Deserialize,
- MallocSizeOf,
- PartialEq,
- Serialize,
- ToAnimatedZero,
- ToResolvedValue,
- ToShmem,
-)]
-pub enum GenericCalcNode<L> {
- /// A leaf node.
- Leaf(L),
- /// A node that negates its children, e.g. Negate(1) == -1.
- Negate(Box<GenericCalcNode<L>>),
- /// A sum node, representing `a + b + c` where a, b, and c are the
- /// arguments.
- Sum(crate::OwnedSlice<GenericCalcNode<L>>),
- /// A `min` or `max` function.
- MinMax(crate::OwnedSlice<GenericCalcNode<L>>, MinMaxOp),
- /// A `clamp()` function.
- Clamp {
- /// The minimum value.
- min: Box<GenericCalcNode<L>>,
- /// The central value.
- center: Box<GenericCalcNode<L>>,
- /// The maximum value.
- max: Box<GenericCalcNode<L>>,
- },
- /// A `round()` function.
- Round {
- /// The rounding strategy.
- strategy: RoundingStrategy,
- /// The value to round.
- value: Box<GenericCalcNode<L>>,
- /// The step value.
- step: Box<GenericCalcNode<L>>,
- },
- /// A `mod()` or `rem()` function.
- ModRem {
- /// The dividend calculation.
- dividend: Box<GenericCalcNode<L>>,
- /// The divisor calculation.
- divisor: Box<GenericCalcNode<L>>,
- /// Is the function mod or rem?
- op: ModRemOp,
- },
- /// A `hypot()` function
- Hypot(crate::OwnedSlice<GenericCalcNode<L>>),
-}
-
-pub use self::GenericCalcNode as CalcNode;
-
-/// A trait that represents all the stuff a valid leaf of a calc expression.
-pub trait CalcNodeLeaf: Clone + Sized + PartialOrd + PartialEq + ToCss {
- /// Returns the unitless value of this leaf.
- fn unitless_value(&self) -> f32;
-
- /// Whether this value is known-negative.
- fn is_negative(&self) -> bool {
- self.unitless_value().is_sign_negative()
- }
-
- /// Whether this value is infinite.
- fn is_infinite(&self) -> bool {
- self.unitless_value().is_infinite()
- }
-
- /// Whether this value is zero.
- fn is_zero(&self) -> bool {
- self.unitless_value().is_zero()
- }
-
- /// Whether this value is NaN.
- fn is_nan(&self) -> bool {
- self.unitless_value().is_nan()
- }
-
- /// Tries to merge one sum to another, that is, perform `x` + `y`.
- fn try_sum_in_place(&mut self, other: &Self) -> Result<(), ()>;
-
- /// Tries a generic arithmetic operation.
- fn try_op<O>(&self, other: &Self, op: O) -> Result<Self, ()>
- where
- O: Fn(f32, f32) -> f32;
-
- /// Map the value of this node with the given operation.
- fn map(&mut self, op: impl FnMut(f32) -> f32);
-
- /// Negates the leaf.
- fn negate(&mut self) {
- self.map(std::ops::Neg::neg);
- }
-
- /// Canonicalizes the expression if necessary.
- fn simplify(&mut self);
-
- /// Returns the sort key for simplification.
- fn sort_key(&self) -> SortKey;
-}
-
-/// The level of any argument being serialized in `to_css_impl`.
-enum ArgumentLevel {
- /// The root of a calculation tree.
- CalculationRoot,
- /// The root of an operand node's argument, e.g. `min(10, 20)`, `10` and `20` will have this
- /// level, but min in this case will have `TopMost`.
- ArgumentRoot,
- /// Any other values serialized in the tree.
- Nested,
-}
-
-impl<L: CalcNodeLeaf> CalcNode<L> {
- /// Negate the node inline. If the node is distributive, it is replaced by the result,
- /// otherwise the node is wrapped in a [`Negate`] node.
- pub fn negate(&mut self) {
- match *self {
- CalcNode::Leaf(ref mut leaf) => leaf.map(|l| l.neg()),
- CalcNode::Negate(ref mut value) => {
- // Don't negate the value here. Replace `self` with it's child.
- let result = mem::replace(
- value.as_mut(),
- Self::MinMax(Default::default(), MinMaxOp::Max),
- );
- *self = result;
- },
- CalcNode::Sum(ref mut children) => {
- for child in children.iter_mut() {
- child.negate();
- }
- },
- CalcNode::MinMax(ref mut children, ref mut op) => {
- for child in children.iter_mut() {
- child.negate();
- }
-
- // Negating min-max means the operation is swapped.
- *op = match *op {
- MinMaxOp::Min => MinMaxOp::Max,
- MinMaxOp::Max => MinMaxOp::Min,
- };
- },
- CalcNode::Clamp {
- ref mut min,
- ref mut center,
- ref mut max,
- } => {
- min.negate();
- center.negate();
- max.negate();
-
- mem::swap(min, max);
- },
- CalcNode::Round {
- ref mut value,
- ref mut step,
- ..
- } => {
- value.negate();
- step.negate();
- },
- CalcNode::ModRem {
- ref mut dividend,
- ref mut divisor,
- ..
- } => {
- dividend.negate();
- divisor.negate();
- },
- CalcNode::Hypot(ref mut children) => {
- for child in children.iter_mut() {
- child.negate();
- }
- },
- }
- }
-
- fn sort_key(&self) -> SortKey {
- match *self {
- Self::Leaf(ref l) => l.sort_key(),
- _ => SortKey::Other,
- }
- }
-
- /// Returns the leaf if we can (if simplification has allowed it).
- pub fn as_leaf(&self) -> Option<&L> {
- match *self {
- Self::Leaf(ref l) => Some(l),
- _ => None,
- }
- }
-
- /// Tries to merge one sum to another, that is, perform `x` + `y`.
- fn try_sum_in_place(&mut self, other: &Self) -> Result<(), ()> {
- match (self, other) {
- (&mut CalcNode::Leaf(ref mut one), &CalcNode::Leaf(ref other)) => {
- one.try_sum_in_place(other)
- },
- _ => Err(()),
- }
- }
-
- /// Tries to apply a generic arithmentic operator
- fn try_op<O>(&self, other: &Self, op: O) -> Result<Self, ()>
- where
- O: Fn(f32, f32) -> f32,
- {
- match (self, other) {
- (&CalcNode::Leaf(ref one), &CalcNode::Leaf(ref other)) => {
- Ok(CalcNode::Leaf(one.try_op(other, op)?))
- },
- _ => Err(()),
- }
- }
-
- /// Map the value of this node with the given operation.
- pub fn map(&mut self, mut op: impl FnMut(f32) -> f32) {
- fn map_internal<L: CalcNodeLeaf>(node: &mut CalcNode<L>, op: &mut impl FnMut(f32) -> f32) {
- match node {
- CalcNode::Leaf(l) => l.map(op),
- CalcNode::Negate(v) => map_internal(v, op),
- CalcNode::Sum(children) => {
- for node in &mut **children {
- map_internal(node, op);
- }
- },
- CalcNode::MinMax(children, _) => {
- for node in &mut **children {
- map_internal(node, op);
- }
- },
- CalcNode::Clamp { min, center, max } => {
- map_internal(min, op);
- map_internal(center, op);
- map_internal(max, op);
- },
- CalcNode::Round { value, step, .. } => {
- map_internal(value, op);
- map_internal(step, op);
- },
- CalcNode::ModRem {
- dividend, divisor, ..
- } => {
- map_internal(dividend, op);
- map_internal(divisor, op);
- },
- CalcNode::Hypot(children) => {
- for node in &mut **children {
- map_internal(node, op);
- }
- },
- }
- }
-
- map_internal(self, &mut op);
- }
-
- /// Convert this `CalcNode` into a `CalcNode` with a different leaf kind.
- pub fn map_leaves<O, F>(&self, mut map: F) -> CalcNode<O>
- where
- O: CalcNodeLeaf,
- F: FnMut(&L) -> O,
- {
- self.map_leaves_internal(&mut map)
- }
-
- fn map_leaves_internal<O, F>(&self, map: &mut F) -> CalcNode<O>
- where
- O: CalcNodeLeaf,
- F: FnMut(&L) -> O,
- {
- fn map_children<L, O, F>(
- children: &[CalcNode<L>],
- map: &mut F,
- ) -> crate::OwnedSlice<CalcNode<O>>
- where
- L: CalcNodeLeaf,
- O: CalcNodeLeaf,
- F: FnMut(&L) -> O,
- {
- children
- .iter()
- .map(|c| c.map_leaves_internal(map))
- .collect()
- }
-
- match *self {
- Self::Leaf(ref l) => CalcNode::Leaf(map(l)),
- Self::Negate(ref c) => CalcNode::Negate(Box::new(c.map_leaves_internal(map))),
- Self::Sum(ref c) => CalcNode::Sum(map_children(c, map)),
- Self::MinMax(ref c, op) => CalcNode::MinMax(map_children(c, map), op),
- Self::Clamp {
- ref min,
- ref center,
- ref max,
- } => {
- let min = Box::new(min.map_leaves_internal(map));
- let center = Box::new(center.map_leaves_internal(map));
- let max = Box::new(max.map_leaves_internal(map));
- CalcNode::Clamp { min, center, max }
- },
- Self::Round {
- strategy,
- ref value,
- ref step,
- } => {
- let value = Box::new(value.map_leaves_internal(map));
- let step = Box::new(step.map_leaves_internal(map));
- CalcNode::Round {
- strategy,
- value,
- step,
- }
- },
- Self::ModRem {
- ref dividend,
- ref divisor,
- op,
- } => {
- let dividend = Box::new(dividend.map_leaves_internal(map));
- let divisor = Box::new(divisor.map_leaves_internal(map));
- CalcNode::ModRem {
- dividend,
- divisor,
- op,
- }
- },
- Self::Hypot(ref c) => CalcNode::Hypot(map_children(c, map)),
- }
- }
-
- /// Resolves the expression returning a value of `O`, given a function to
- /// turn a leaf into the relevant value.
- pub fn resolve<O>(
- &self,
- mut leaf_to_output_fn: impl FnMut(&L) -> Result<O, ()>,
- ) -> Result<O, ()>
- where
- O: PartialOrd
- + PartialEq
- + Add<Output = O>
- + Mul<Output = O>
- + Div<Output = O>
- + Sub<Output = O>
- + Zero
- + Float
- + Copy,
- {
- self.resolve_internal(&mut leaf_to_output_fn)
- }
-
- fn resolve_internal<O, F>(&self, leaf_to_output_fn: &mut F) -> Result<O, ()>
- where
- O: PartialOrd
- + PartialEq
- + Add<Output = O>
- + Mul<Output = O>
- + Div<Output = O>
- + Sub<Output = O>
- + Zero
- + Float
- + Copy,
- F: FnMut(&L) -> Result<O, ()>,
- {
- Ok(match *self {
- Self::Leaf(ref l) => return leaf_to_output_fn(l),
- Self::Negate(ref c) => c.resolve_internal(leaf_to_output_fn)?.neg(),
- Self::Sum(ref c) => {
- let mut result = Zero::zero();
- for child in &**c {
- result = result + child.resolve_internal(leaf_to_output_fn)?;
- }
- result
- },
- Self::MinMax(ref nodes, op) => {
- let mut result = nodes[0].resolve_internal(leaf_to_output_fn)?;
-
- if result.is_nan() {
- return Ok(result);
- }
-
- for node in nodes.iter().skip(1) {
- let candidate = node.resolve_internal(leaf_to_output_fn)?;
-
- if candidate.is_nan() {
- result = candidate;
- break;
- }
-
- let candidate_wins = match op {
- MinMaxOp::Min => candidate < result,
- MinMaxOp::Max => candidate > result,
- };
- if candidate_wins {
- result = candidate;
- }
- }
- result
- },
- Self::Clamp {
- ref min,
- ref center,
- ref max,
- } => {
- let min = min.resolve_internal(leaf_to_output_fn)?;
- let center = center.resolve_internal(leaf_to_output_fn)?;
- let max = max.resolve_internal(leaf_to_output_fn)?;
-
- let mut result = center;
- if result > max {
- result = max;
- }
- if result < min {
- result = min
- }
-
- if min.is_nan() || center.is_nan() || max.is_nan() {
- result = <O as Float>::nan();
- }
-
- result
- },
- Self::Round {
- strategy,
- ref value,
- ref step,
- } => {
- let value = value.resolve_internal(leaf_to_output_fn)?;
- let step = step.resolve_internal(leaf_to_output_fn)?;
-
- // TODO(emilio): Seems like at least a few of these
- // special-cases could be removed if we do the math in a
- // particular order.
- if step.is_zero() {
- return Ok(<O as Float>::nan());
- }
-
- if value.is_infinite() && step.is_infinite() {
- return Ok(<O as Float>::nan());
- }
-
- if value.is_infinite() {
- return Ok(value);
- }
-
- if step.is_infinite() {
- match strategy {
- RoundingStrategy::Nearest | RoundingStrategy::ToZero => {
- return if value.is_sign_negative() {
- Ok(<O as Float>::neg_zero())
- } else {
- Ok(<O as Zero>::zero())
- }
- },
- RoundingStrategy::Up => {
- return if !value.is_sign_negative() && !value.is_zero() {
- Ok(<O as Float>::infinity())
- } else if !value.is_sign_negative() && value.is_zero() {
- Ok(value)
- } else {
- Ok(<O as Float>::neg_zero())
- }
- },
- RoundingStrategy::Down => {
- return if value.is_sign_negative() && !value.is_zero() {
- Ok(<O as Float>::neg_infinity())
- } else if value.is_sign_negative() && value.is_zero() {
- Ok(value)
- } else {
- Ok(<O as Zero>::zero())
- }
- },
- }
- }
-
- let div = value / step;
- let lower_bound = div.floor() * step;
- let upper_bound = div.ceil() * step;
-
- match strategy {
- RoundingStrategy::Nearest => {
- // In case of a tie, use the upper bound
- if value - lower_bound < upper_bound - value {
- lower_bound
- } else {
- upper_bound
- }
- },
- RoundingStrategy::Up => upper_bound,
- RoundingStrategy::Down => lower_bound,
- RoundingStrategy::ToZero => {
- // In case of a tie, use the upper bound
- if lower_bound.abs() < upper_bound.abs() {
- lower_bound
- } else {
- upper_bound
- }
- },
- }
- },
- Self::ModRem {
- ref dividend,
- ref divisor,
- op,
- } => {
- let dividend = dividend.resolve_internal(leaf_to_output_fn)?;
- let divisor = divisor.resolve_internal(leaf_to_output_fn)?;
-
- // In mod(A, B) only, if B is infinite and A has opposite sign to B
- // (including an oppositely-signed zero), the result is NaN.
- // https://drafts.csswg.org/css-values/#round-infinities
- if matches!(op, ModRemOp::Mod) &&
- divisor.is_infinite() &&
- dividend.is_sign_negative() != divisor.is_sign_negative()
- {
- return Ok(<O as Float>::nan());
- }
-
- match op {
- ModRemOp::Mod => dividend - divisor * (dividend / divisor).floor(),
- ModRemOp::Rem => dividend - divisor * (dividend / divisor).trunc(),
- }
- },
- Self::Hypot(ref c) => {
- let mut result: O = Zero::zero();
- for child in &**c {
- result = result + child.resolve_internal(leaf_to_output_fn)?.powi(2);
- }
- result.sqrt()
- },
- })
- }
-
- fn is_negative_leaf(&self) -> bool {
- match *self {
- Self::Leaf(ref l) => l.is_negative(),
- _ => false,
- }
- }
-
- fn is_zero_leaf(&self) -> bool {
- match *self {
- Self::Leaf(ref l) => l.is_zero(),
- _ => false,
- }
- }
-
- fn is_infinite_leaf(&self) -> bool {
- match *self {
- Self::Leaf(ref l) => l.is_infinite(),
- _ => false,
- }
- }
-
- /// Multiplies the node by a scalar.
- pub fn mul_by(&mut self, scalar: f32) {
- match *self {
- Self::Leaf(ref mut l) => l.map(|v| v * scalar),
- Self::Negate(ref mut value) => value.mul_by(scalar),
- // Multiplication is distributive across this.
- Self::Sum(ref mut children) => {
- for node in &mut **children {
- node.mul_by(scalar);
- }
- },
- // This one is a bit trickier.
- Self::MinMax(ref mut children, ref mut op) => {
- for node in &mut **children {
- node.mul_by(scalar);
- }
-
- // For negatives we need to invert the operation.
- if scalar < 0. {
- *op = match *op {
- MinMaxOp::Min => MinMaxOp::Max,
- MinMaxOp::Max => MinMaxOp::Min,
- }
- }
- },
- // This one is slightly tricky too.
- Self::Clamp {
- ref mut min,
- ref mut center,
- ref mut max,
- } => {
- min.mul_by(scalar);
- center.mul_by(scalar);
- max.mul_by(scalar);
- // For negatives we need to swap min / max.
- if scalar < 0. {
- mem::swap(min, max);
- }
- },
- Self::Round {
- ref mut value,
- ref mut step,
- ..
- } => {
- value.mul_by(scalar);
- step.mul_by(scalar);
- },
- Self::ModRem {
- ref mut dividend,
- ref mut divisor,
- ..
- } => {
- dividend.mul_by(scalar);
- divisor.mul_by(scalar);
- },
- // Not possible to handle negatives in this case, see: https://bugzil.la/1815448
- Self::Hypot(ref mut children) => {
- for node in &mut **children {
- node.mul_by(scalar);
- }
- },
- }
- }
-
- /// Visits all the nodes in this calculation tree recursively, starting by
- /// the leaves and bubbling all the way up.
- ///
- /// This is useful for simplification, but can also be used for validation
- /// and such.
- pub fn visit_depth_first(&mut self, mut f: impl FnMut(&mut Self)) {
- self.visit_depth_first_internal(&mut f);
- }
-
- fn visit_depth_first_internal(&mut self, f: &mut impl FnMut(&mut Self)) {
- match *self {
- Self::Clamp {
- ref mut min,
- ref mut center,
- ref mut max,
- } => {
- min.visit_depth_first_internal(f);
- center.visit_depth_first_internal(f);
- max.visit_depth_first_internal(f);
- },
- Self::Round {
- ref mut value,
- ref mut step,
- ..
- } => {
- value.visit_depth_first_internal(f);
- step.visit_depth_first_internal(f);
- },
- Self::ModRem {
- ref mut dividend,
- ref mut divisor,
- ..
- } => {
- dividend.visit_depth_first_internal(f);
- divisor.visit_depth_first_internal(f);
- },
- Self::Sum(ref mut children) |
- Self::MinMax(ref mut children, _) |
- Self::Hypot(ref mut children) => {
- for child in &mut **children {
- child.visit_depth_first_internal(f);
- }
- },
- Self::Negate(ref mut value) => {
- value.visit_depth_first_internal(f);
- },
- Self::Leaf(..) => {},
- }
- f(self);
- }
-
- /// Simplifies and sorts the calculation of a given node. All the nodes
- /// below it should be simplified already, this only takes care of
- /// simplifying directly nested nodes. So, probably should always be used in
- /// combination with `visit_depth_first()`.
- ///
- /// This is only needed if it's going to be preserved after parsing (so, for
- /// `<length-percentage>`). Otherwise we can just evaluate it using
- /// `resolve()`, and we'll come up with a simplified value anyways.
- ///
- /// <https://drafts.csswg.org/css-values-4/#calc-simplification>
- pub fn simplify_and_sort_direct_children(&mut self) {
- macro_rules! replace_self_with {
- ($slot:expr) => {{
- let dummy = Self::MinMax(Default::default(), MinMaxOp::Max);
- let result = mem::replace($slot, dummy);
- *self = result;
- }};
- }
- match *self {
- Self::Clamp {
- ref mut min,
- ref mut center,
- ref mut max,
- } => {
- // NOTE: clamp() is max(min, min(center, max))
- let min_cmp_center = match min.partial_cmp(&center) {
- Some(o) => o,
- None => return,
- };
-
- // So if we can prove that min is more than center, then we won,
- // as that's what we should always return.
- if matches!(min_cmp_center, cmp::Ordering::Greater) {
- return replace_self_with!(&mut **min);
- }
-
- // Otherwise try with max.
- let max_cmp_center = match max.partial_cmp(&center) {
- Some(o) => o,
- None => return,
- };
-
- if matches!(max_cmp_center, cmp::Ordering::Less) {
- // max is less than center, so we need to return effectively
- // `max(min, max)`.
- let max_cmp_min = match max.partial_cmp(&min) {
- Some(o) => o,
- None => {
- debug_assert!(
- false,
- "We compared center with min and max, how are \
- min / max not comparable with each other?"
- );
- return;
- },
- };
-
- if matches!(max_cmp_min, cmp::Ordering::Less) {
- return replace_self_with!(&mut **min);
- }
-
- return replace_self_with!(&mut **max);
- }
-
- // Otherwise we're the center node.
- return replace_self_with!(&mut **center);
- },
- Self::Round {
- strategy,
- ref mut value,
- ref mut step,
- } => {
- if step.is_zero_leaf() {
- value.mul_by(f32::NAN);
- return replace_self_with!(&mut **value);
- }
-
- if value.is_infinite_leaf() && step.is_infinite_leaf() {
- value.mul_by(f32::NAN);
- return replace_self_with!(&mut **value);
- }
-
- if value.is_infinite_leaf() {
- return replace_self_with!(&mut **value);
- }
-
- if step.is_infinite_leaf() {
- match strategy {
- RoundingStrategy::Nearest | RoundingStrategy::ToZero => {
- value.mul_by(0.);
- return replace_self_with!(&mut **value);
- },
- RoundingStrategy::Up => {
- if !value.is_negative_leaf() && !value.is_zero_leaf() {
- value.mul_by(f32::INFINITY);
- return replace_self_with!(&mut **value);
- } else if !value.is_negative_leaf() && value.is_zero_leaf() {
- return replace_self_with!(&mut **value);
- } else {
- value.mul_by(0.);
- return replace_self_with!(&mut **value);
- }
- },
- RoundingStrategy::Down => {
- if value.is_negative_leaf() && !value.is_zero_leaf() {
- value.mul_by(f32::INFINITY);
- return replace_self_with!(&mut **value);
- } else if value.is_negative_leaf() && value.is_zero_leaf() {
- return replace_self_with!(&mut **value);
- } else {
- value.mul_by(0.);
- return replace_self_with!(&mut **value);
- }
- },
- }
- }
-
- if step.is_negative_leaf() {
- step.negate();
- }
-
- let remainder = match value.try_op(step, Rem::rem) {
- Ok(res) => res,
- Err(..) => return,
- };
-
- let (mut lower_bound, mut upper_bound) = if value.is_negative_leaf() {
- let upper_bound = match value.try_op(&remainder, Sub::sub) {
- Ok(res) => res,
- Err(..) => return,
- };
-
- let lower_bound = match upper_bound.try_op(&step, Sub::sub) {
- Ok(res) => res,
- Err(..) => return,
- };
-
- (lower_bound, upper_bound)
- } else {
- let lower_bound = match value.try_op(&remainder, Sub::sub) {
- Ok(res) => res,
- Err(..) => return,
- };
-
- let upper_bound = match lower_bound.try_op(&step, Add::add) {
- Ok(res) => res,
- Err(..) => return,
- };
-
- (lower_bound, upper_bound)
- };
-
- match strategy {
- RoundingStrategy::Nearest => {
- let lower_diff = match value.try_op(&lower_bound, Sub::sub) {
- Ok(res) => res,
- Err(..) => return,
- };
-
- let upper_diff = match upper_bound.try_op(value, Sub::sub) {
- Ok(res) => res,
- Err(..) => return,
- };
-
- // In case of a tie, use the upper bound
- if lower_diff < upper_diff {
- return replace_self_with!(&mut lower_bound);
- } else {
- return replace_self_with!(&mut upper_bound);
- }
- },
- RoundingStrategy::Up => return replace_self_with!(&mut upper_bound),
- RoundingStrategy::Down => return replace_self_with!(&mut lower_bound),
- RoundingStrategy::ToZero => {
- let mut lower_diff = lower_bound.clone();
- let mut upper_diff = upper_bound.clone();
-
- if lower_diff.is_negative_leaf() {
- lower_diff.negate();
- }
-
- if upper_diff.is_negative_leaf() {
- upper_diff.negate();
- }
-
- // In case of a tie, use the upper bound
- if lower_diff < upper_diff {
- return replace_self_with!(&mut lower_bound);
- } else {
- return replace_self_with!(&mut upper_bound);
- }
- },
- };
- },
- Self::ModRem {
- ref dividend,
- ref divisor,
- op,
- } => {
- let mut result = dividend.clone();
-
- // In mod(A, B) only, if B is infinite and A has opposite sign to B
- // (including an oppositely-signed zero), the result is NaN.
- // https://drafts.csswg.org/css-values/#round-infinities
- if matches!(op, ModRemOp::Mod) &&
- divisor.is_infinite_leaf() &&
- dividend.is_negative_leaf() != divisor.is_negative_leaf()
- {
- result.mul_by(f32::NAN);
- return replace_self_with!(&mut *result);
- }
-
- let result = match op {
- ModRemOp::Mod => dividend.try_op(divisor, |a, b| a - b * (a / b).floor()),
- ModRemOp::Rem => dividend.try_op(divisor, |a, b| a - b * (a / b).trunc()),
- };
-
- let mut result = match result {
- Ok(res) => res,
- Err(..) => return,
- };
-
- return replace_self_with!(&mut result);
- },
- Self::MinMax(ref mut children, op) => {
- let winning_order = match op {
- MinMaxOp::Min => cmp::Ordering::Less,
- MinMaxOp::Max => cmp::Ordering::Greater,
- };
-
- let mut result = 0;
- for i in 1..children.len() {
- let o = match children[i].partial_cmp(&children[result]) {
- // We can't compare all the children, so we can't
- // know which one will actually win. Bail out and
- // keep ourselves as a min / max function.
- //
- // TODO: Maybe we could simplify compatible children,
- // see https://github.com/w3c/csswg-drafts/issues/4756
- None => return,
- Some(o) => o,
- };
-
- if o == winning_order {
- result = i;
- }
- }
-
- replace_self_with!(&mut children[result]);
- },
- Self::Sum(ref mut children_slot) => {
- let mut sums_to_merge = SmallVec::<[_; 3]>::new();
- let mut extra_kids = 0;
- for (i, child) in children_slot.iter().enumerate() {
- if let Self::Sum(ref children) = *child {
- extra_kids += children.len();
- sums_to_merge.push(i);
- }
- }
-
- // If we only have one kid, we've already simplified it, and it
- // doesn't really matter whether it's a sum already or not, so
- // lift it up and continue.
- if children_slot.len() == 1 {
- return replace_self_with!(&mut children_slot[0]);
- }
-
- let mut children = mem::replace(children_slot, Default::default()).into_vec();
-
- if !sums_to_merge.is_empty() {
- children.reserve(extra_kids - sums_to_merge.len());
- // Merge all our nested sums, in reverse order so that the
- // list indices are not invalidated.
- for i in sums_to_merge.drain(..).rev() {
- let kid_children = match children.swap_remove(i) {
- Self::Sum(c) => c,
- _ => unreachable!(),
- };
-
- // This would be nicer with
- // https://github.com/rust-lang/rust/issues/59878 fixed.
- children.extend(kid_children.into_vec());
- }
- }
-
- debug_assert!(children.len() >= 2, "Should still have multiple kids!");
-
- // Sort by spec order.
- children.sort_unstable_by_key(|c| c.sort_key());
-
- // NOTE: if the function returns true, by the docs of dedup_by,
- // a is removed.
- children.dedup_by(|a, b| b.try_sum_in_place(a).is_ok());
-
- if children.len() == 1 {
- // If only one children remains, lift it up, and carry on.
- replace_self_with!(&mut children[0]);
- } else {
- // Else put our simplified children back.
- *children_slot = children.into_boxed_slice().into();
- }
- },
- Self::Hypot(ref children) => {
- let mut result = match children[0].try_op(&children[0], Mul::mul) {
- Ok(res) => res,
- Err(..) => return,
- };
-
- for child in children.iter().skip(1) {
- let square = match child.try_op(&child, Mul::mul) {
- Ok(res) => res,
- Err(..) => return,
- };
- result = match result.try_op(&square, Add::add) {
- Ok(res) => res,
- Err(..) => return,
- }
- }
-
- result = match result.try_op(&result, |a, _| a.sqrt()) {
- Ok(res) => res,
- Err(..) => return,
- };
-
- replace_self_with!(&mut result);
- },
- Self::Negate(ref mut child) => {
- // Step 6.
- match &mut **child {
- CalcNode::Leaf(_) => {
- // 1. If root’s child is a numeric value, return an equivalent numeric value, but
- // with the value negated (0 - value).
- child.negate();
- replace_self_with!(&mut **child);
- },
- CalcNode::Negate(value) => {
- // 2. If root’s child is a Negate node, return the child’s child.
- replace_self_with!(&mut **value);
- },
- _ => {
- // 3. Return root.
- },
- }
- },
- Self::Leaf(ref mut l) => {
- l.simplify();
- },
- }
- }
-
- /// Simplifies and sorts the kids in the whole calculation subtree.
- pub fn simplify_and_sort(&mut self) {
- self.visit_depth_first(|node| node.simplify_and_sort_direct_children())
- }
-
- fn to_css_impl<W>(&self, dest: &mut CssWriter<W>, level: ArgumentLevel) -> fmt::Result
- where
- W: Write,
- {
- let write_closing_paren = match *self {
- Self::MinMax(_, op) => {
- dest.write_str(match op {
- MinMaxOp::Max => "max(",
- MinMaxOp::Min => "min(",
- })?;
- true
- },
- Self::Clamp { .. } => {
- dest.write_str("clamp(")?;
- true
- },
- Self::Round { strategy, .. } => {
- match strategy {
- RoundingStrategy::Nearest => dest.write_str("round("),
- RoundingStrategy::Up => dest.write_str("round(up, "),
- RoundingStrategy::Down => dest.write_str("round(down, "),
- RoundingStrategy::ToZero => dest.write_str("round(to-zero, "),
- }?;
-
- true
- },
- Self::ModRem { op, .. } => {
- dest.write_str(match op {
- ModRemOp::Mod => "mod(",
- ModRemOp::Rem => "rem(",
- })?;
-
- true
- },
- Self::Hypot(_) => {
- dest.write_str("hypot(")?;
- true
- },
- Self::Negate(_) => {
- // We never generate a [`Negate`] node as the root of a calculation, only inside
- // [`Sum`] nodes as a child. Because negate nodes are handled by the [`Sum`] node
- // directly (see below), this node will never be serialized.
- debug_assert!(
- false,
- "We never serialize Negate nodes as they are handled inside Sum nodes."
- );
- dest.write_str("(-1 * ")?;
- true
- },
- Self::Sum(_) => match level {
- ArgumentLevel::CalculationRoot => {
- dest.write_str("calc(")?;
- true
- },
- ArgumentLevel::ArgumentRoot => false,
- ArgumentLevel::Nested => {
- dest.write_str("(")?;
- true
- },
- },
- Self::Leaf(_) => match level {
- ArgumentLevel::CalculationRoot => {
- dest.write_str("calc(")?;
- true
- },
- ArgumentLevel::ArgumentRoot | ArgumentLevel::Nested => false,
- },
- };
-
- match *self {
- Self::MinMax(ref children, _) | Self::Hypot(ref children) => {
- let mut first = true;
- for child in &**children {
- if !first {
- dest.write_str(", ")?;
- }
- first = false;
- child.to_css_impl(dest, ArgumentLevel::ArgumentRoot)?;
- }
- },
- Self::Negate(ref value) => value.to_css_impl(dest, ArgumentLevel::Nested)?,
- Self::Sum(ref children) => {
- let mut first = true;
- for child in &**children {
- if !first {
- match child {
- Self::Leaf(l) => {
- if l.is_negative() {
- dest.write_str(" - ")?;
- let mut negated = l.clone();
- negated.negate();
- negated.to_css(dest)?;
- } else {
- dest.write_str(" + ")?;
- l.to_css(dest)?;
- }
- },
- Self::Negate(n) => {
- dest.write_str(" - ")?;
- n.to_css_impl(dest, ArgumentLevel::Nested)?;
- },
- _ => {
- dest.write_str(" + ")?;
- child.to_css_impl(dest, ArgumentLevel::Nested)?;
- },
- }
- } else {
- first = false;
- child.to_css_impl(dest, ArgumentLevel::Nested)?;
- }
- }
- },
- Self::Clamp {
- ref min,
- ref center,
- ref max,
- } => {
- min.to_css_impl(dest, ArgumentLevel::ArgumentRoot)?;
- dest.write_str(", ")?;
- center.to_css_impl(dest, ArgumentLevel::ArgumentRoot)?;
- dest.write_str(", ")?;
- max.to_css_impl(dest, ArgumentLevel::ArgumentRoot)?;
- },
- Self::Round {
- ref value,
- ref step,
- ..
- } => {
- value.to_css_impl(dest, ArgumentLevel::ArgumentRoot)?;
- dest.write_str(", ")?;
- step.to_css_impl(dest, ArgumentLevel::ArgumentRoot)?;
- },
- Self::ModRem {
- ref dividend,
- ref divisor,
- ..
- } => {
- dividend.to_css_impl(dest, ArgumentLevel::ArgumentRoot)?;
- dest.write_str(", ")?;
- divisor.to_css_impl(dest, ArgumentLevel::ArgumentRoot)?;
- },
- Self::Leaf(ref l) => l.to_css(dest)?,
- }
-
- if write_closing_paren {
- dest.write_char(')')?;
- }
- Ok(())
- }
-}
-
-impl<L: CalcNodeLeaf> PartialOrd for CalcNode<L> {
- fn partial_cmp(&self, other: &Self) -> Option<cmp::Ordering> {
- match (self, other) {
- (&CalcNode::Leaf(ref one), &CalcNode::Leaf(ref other)) => one.partial_cmp(other),
- _ => None,
- }
- }
-}
-
-impl<L: CalcNodeLeaf> ToCss for CalcNode<L> {
- /// <https://drafts.csswg.org/css-values/#calc-serialize>
- fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
- where
- W: Write,
- {
- self.to_css_impl(dest, ArgumentLevel::CalculationRoot)
- }
-}
diff --git a/components/style/values/generics/color.rs b/components/style/values/generics/color.rs
deleted file mode 100644
index d143e9d3c86..00000000000
--- a/components/style/values/generics/color.rs
+++ /dev/null
@@ -1,196 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
-
-//! Generic types for color properties.
-
-use crate::color::mix::ColorInterpolationMethod;
-use crate::color::AbsoluteColor;
-use crate::values::specified::percentage::ToPercentage;
-use std::fmt::{self, Write};
-use style_traits::{CssWriter, ToCss};
-
-/// This struct represents a combined color from a numeric color and
-/// the current foreground color (currentcolor keyword).
-#[derive(Clone, Debug, MallocSizeOf, PartialEq, ToAnimatedValue, ToShmem)]
-#[repr(C)]
-pub enum GenericColor<Percentage> {
- /// The actual numeric color.
- Absolute(AbsoluteColor),
- /// The `CurrentColor` keyword.
- CurrentColor,
- /// The color-mix() function.
- ColorMix(Box<GenericColorMix<Self, Percentage>>),
-}
-
-/// A restricted version of the css `color-mix()` function, which only supports
-/// percentages.
-///
-/// https://drafts.csswg.org/css-color-5/#color-mix
-#[derive(
- Clone,
- Debug,
- MallocSizeOf,
- PartialEq,
- ToAnimatedValue,
- ToComputedValue,
- ToResolvedValue,
- ToShmem,
-)]
-#[allow(missing_docs)]
-#[repr(C)]
-pub struct GenericColorMix<Color, Percentage> {
- pub interpolation: ColorInterpolationMethod,
- pub left: Color,
- pub left_percentage: Percentage,
- pub right: Color,
- pub right_percentage: Percentage,
- pub normalize_weights: bool,
-}
-
-pub use self::GenericColorMix as ColorMix;
-
-impl<Color: ToCss, Percentage: ToCss + ToPercentage> ToCss for ColorMix<Color, Percentage> {
- fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
- where
- W: Write,
- {
- fn can_omit<Percentage: ToPercentage>(
- percent: &Percentage,
- other: &Percentage,
- is_left: bool,
- ) -> bool {
- if percent.is_calc() {
- return false;
- }
- if percent.to_percentage() == 0.5 {
- return other.to_percentage() == 0.5;
- }
- if is_left {
- return false;
- }
- (1.0 - percent.to_percentage() - other.to_percentage()).abs() <= f32::EPSILON
- }
-
- dest.write_str("color-mix(")?;
- self.interpolation.to_css(dest)?;
- dest.write_str(", ")?;
- self.left.to_css(dest)?;
- if !can_omit(&self.left_percentage, &self.right_percentage, true) {
- dest.write_char(' ')?;
- self.left_percentage.to_css(dest)?;
- }
- dest.write_str(", ")?;
- self.right.to_css(dest)?;
- if !can_omit(&self.right_percentage, &self.left_percentage, false) {
- dest.write_char(' ')?;
- self.right_percentage.to_css(dest)?;
- }
- dest.write_char(')')
- }
-}
-
-impl<Percentage> ColorMix<GenericColor<Percentage>, Percentage> {
- /// Mix the colors so that we get a single color. If any of the 2 colors are
- /// not mixable (perhaps not absolute?), then return None.
- pub fn mix_to_absolute(&self) -> Option<AbsoluteColor>
- where
- Percentage: ToPercentage,
- {
- let left = self.left.as_absolute()?;
- let right = self.right.as_absolute()?;
-
- Some(crate::color::mix::mix(
- self.interpolation,
- &left,
- self.left_percentage.to_percentage(),
- &right,
- self.right_percentage.to_percentage(),
- self.normalize_weights,
- ))
- }
-}
-
-pub use self::GenericColor as Color;
-
-impl<Percentage> Color<Percentage> {
- /// If this color is absolute return it's value, otherwise return None.
- pub fn as_absolute(&self) -> Option<&AbsoluteColor> {
- match *self {
- Self::Absolute(ref absolute) => Some(absolute),
- _ => None,
- }
- }
-
- /// Returns a color value representing currentcolor.
- pub fn currentcolor() -> Self {
- Self::CurrentColor
- }
-
- /// Whether it is a currentcolor value (no numeric color component).
- pub fn is_currentcolor(&self) -> bool {
- matches!(*self, Self::CurrentColor)
- }
-
- /// Whether this color is an absolute color.
- pub fn is_absolute(&self) -> bool {
- matches!(*self, Self::Absolute(..))
- }
-}
-
-/// Either `<color>` or `auto`.
-#[derive(
- Animate,
- Clone,
- ComputeSquaredDistance,
- Copy,
- Debug,
- MallocSizeOf,
- PartialEq,
- Parse,
- SpecifiedValueInfo,
- ToAnimatedValue,
- ToAnimatedZero,
- ToComputedValue,
- ToResolvedValue,
- ToCss,
- ToShmem,
-)]
-#[repr(C, u8)]
-pub enum GenericColorOrAuto<C> {
- /// A `<color>`.
- Color(C),
- /// `auto`
- Auto,
-}
-
-pub use self::GenericColorOrAuto as ColorOrAuto;
-
-/// Caret color is effectively a ColorOrAuto, but resolves `auto` to
-/// currentColor.
-#[derive(
- Animate,
- Clone,
- ComputeSquaredDistance,
- Copy,
- Debug,
- MallocSizeOf,
- PartialEq,
- SpecifiedValueInfo,
- ToAnimatedValue,
- ToAnimatedZero,
- ToComputedValue,
- ToCss,
- ToShmem,
-)]
-#[repr(transparent)]
-pub struct GenericCaretColor<C>(pub GenericColorOrAuto<C>);
-
-impl<C> GenericCaretColor<C> {
- /// Returns the `auto` value.
- pub fn auto() -> Self {
- GenericCaretColor(GenericColorOrAuto::Auto)
- }
-}
-
-pub use self::GenericCaretColor as CaretColor;
diff --git a/components/style/values/generics/column.rs b/components/style/values/generics/column.rs
deleted file mode 100644
index 4b5f0e03993..00000000000
--- a/components/style/values/generics/column.rs
+++ /dev/null
@@ -1,45 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
-
-//! Generic types for the column properties.
-
-/// A generic type for `column-count` values.
-#[derive(
- Animate,
- Clone,
- ComputeSquaredDistance,
- Copy,
- Debug,
- MallocSizeOf,
- Parse,
- PartialEq,
- SpecifiedValueInfo,
- ToAnimatedValue,
- ToAnimatedZero,
- ToComputedValue,
- ToCss,
- ToResolvedValue,
- ToShmem,
-)]
-pub enum ColumnCount<PositiveInteger> {
- /// A positive integer.
- Integer(PositiveInteger),
- /// The keyword `auto`.
- #[animation(error)]
- Auto,
-}
-
-impl<I> ColumnCount<I> {
- /// Returns `auto`.
- #[inline]
- pub fn auto() -> Self {
- ColumnCount::Auto
- }
-
- /// Returns whether this value is `auto`.
- #[inline]
- pub fn is_auto(self) -> bool {
- matches!(self, ColumnCount::Auto)
- }
-}
diff --git a/components/style/values/generics/counters.rs b/components/style/values/generics/counters.rs
deleted file mode 100644
index 3f23c74b334..00000000000
--- a/components/style/values/generics/counters.rs
+++ /dev/null
@@ -1,287 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
-
-//! Generic types for counters-related CSS values.
-
-#[cfg(feature = "servo")]
-use crate::computed_values::list_style_type::T as ListStyleType;
-#[cfg(feature = "gecko")]
-use crate::values::generics::CounterStyle;
-use crate::values::specified::Attr;
-use crate::values::CustomIdent;
-use std::fmt::{self, Write};
-use std::ops::Deref;
-use style_traits::{CssWriter, ToCss};
-
-/// A name / value pair for counters.
-#[derive(
- Clone,
- Debug,
- MallocSizeOf,
- PartialEq,
- SpecifiedValueInfo,
- ToComputedValue,
- ToResolvedValue,
- ToShmem,
-)]
-#[repr(C)]
-pub struct GenericCounterPair<Integer> {
- /// The name of the counter.
- pub name: CustomIdent,
- /// The value of the counter / increment / etc.
- pub value: Integer,
- /// If true, then this represents `reversed(name)`.
- /// NOTE: It can only be true on `counter-reset` values.
- pub is_reversed: bool,
-}
-pub use self::GenericCounterPair as CounterPair;
-
-impl<Integer> ToCss for CounterPair<Integer>
-where
- Integer: ToCss + PartialEq<i32>,
-{
- fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
- where
- W: Write,
- {
- if self.is_reversed {
- dest.write_str("reversed(")?;
- }
- self.name.to_css(dest)?;
- if self.is_reversed {
- dest.write_char(')')?;
- if self.value == i32::min_value() {
- return Ok(());
- }
- }
- dest.write_char(' ')?;
- self.value.to_css(dest)
- }
-}
-
-/// A generic value for the `counter-increment` property.
-#[derive(
- Clone,
- Debug,
- Default,
- MallocSizeOf,
- PartialEq,
- SpecifiedValueInfo,
- ToComputedValue,
- ToCss,
- ToResolvedValue,
- ToShmem,
-)]
-#[repr(transparent)]
-pub struct GenericCounterIncrement<I>(#[css(field_bound)] pub GenericCounters<I>);
-pub use self::GenericCounterIncrement as CounterIncrement;
-
-impl<I> CounterIncrement<I> {
- /// Returns a new value for `counter-increment`.
- #[inline]
- pub fn new(counters: Vec<CounterPair<I>>) -> Self {
- CounterIncrement(Counters(counters.into()))
- }
-}
-
-impl<I> Deref for CounterIncrement<I> {
- type Target = [CounterPair<I>];
-
- #[inline]
- fn deref(&self) -> &Self::Target {
- &(self.0).0
- }
-}
-
-/// A generic value for the `counter-set` property.
-#[derive(
- Clone,
- Debug,
- Default,
- MallocSizeOf,
- PartialEq,
- SpecifiedValueInfo,
- ToComputedValue,
- ToCss,
- ToResolvedValue,
- ToShmem,
-)]
-#[repr(transparent)]
-pub struct GenericCounterSet<I>(#[css(field_bound)] pub GenericCounters<I>);
-pub use self::GenericCounterSet as CounterSet;
-
-impl<I> CounterSet<I> {
- /// Returns a new value for `counter-set`.
- #[inline]
- pub fn new(counters: Vec<CounterPair<I>>) -> Self {
- CounterSet(Counters(counters.into()))
- }
-}
-
-impl<I> Deref for CounterSet<I> {
- type Target = [CounterPair<I>];
-
- #[inline]
- fn deref(&self) -> &Self::Target {
- &(self.0).0
- }
-}
-
-/// A generic value for the `counter-reset` property.
-#[derive(
- Clone,
- Debug,
- Default,
- MallocSizeOf,
- PartialEq,
- SpecifiedValueInfo,
- ToComputedValue,
- ToCss,
- ToResolvedValue,
- ToShmem,
-)]
-#[repr(transparent)]
-pub struct GenericCounterReset<I>(#[css(field_bound)] pub GenericCounters<I>);
-pub use self::GenericCounterReset as CounterReset;
-
-impl<I> CounterReset<I> {
- /// Returns a new value for `counter-reset`.
- #[inline]
- pub fn new(counters: Vec<CounterPair<I>>) -> Self {
- CounterReset(Counters(counters.into()))
- }
-}
-
-impl<I> Deref for CounterReset<I> {
- type Target = [CounterPair<I>];
-
- #[inline]
- fn deref(&self) -> &Self::Target {
- &(self.0).0
- }
-}
-
-/// A generic value for lists of counters.
-///
-/// Keyword `none` is represented by an empty vector.
-#[derive(
- Clone,
- Debug,
- Default,
- MallocSizeOf,
- PartialEq,
- SpecifiedValueInfo,
- ToComputedValue,
- ToCss,
- ToResolvedValue,
- ToShmem,
-)]
-#[repr(transparent)]
-pub struct GenericCounters<I>(
- #[css(field_bound)]
- #[css(iterable, if_empty = "none")]
- crate::OwnedSlice<GenericCounterPair<I>>,
-);
-pub use self::GenericCounters as Counters;
-
-#[cfg(feature = "servo")]
-type CounterStyleType = ListStyleType;
-
-#[cfg(feature = "gecko")]
-type CounterStyleType = CounterStyle;
-
-#[cfg(feature = "servo")]
-#[inline]
-fn is_decimal(counter_type: &CounterStyleType) -> bool {
- *counter_type == ListStyleType::Decimal
-}
-
-#[cfg(feature = "gecko")]
-#[inline]
-fn is_decimal(counter_type: &CounterStyleType) -> bool {
- *counter_type == CounterStyle::decimal()
-}
-
-/// The specified value for the `content` property.
-///
-/// https://drafts.csswg.org/css-content/#propdef-content
-#[derive(
- Clone, Debug, Eq, MallocSizeOf, PartialEq, SpecifiedValueInfo, ToComputedValue, ToCss, ToShmem,
-)]
-#[repr(u8)]
-pub enum GenericContent<Image> {
- /// `normal` reserved keyword.
- Normal,
- /// `none` reserved keyword.
- None,
- /// Content items.
- Items(#[css(iterable)] crate::OwnedSlice<GenericContentItem<Image>>),
-}
-
-pub use self::GenericContent as Content;
-
-impl<Image> Content<Image> {
- /// Whether `self` represents list of items.
- #[inline]
- pub fn is_items(&self) -> bool {
- matches!(*self, Self::Items(..))
- }
-
- /// Set `content` property to `normal`.
- #[inline]
- pub fn normal() -> Self {
- Content::Normal
- }
-}
-
-/// Items for the `content` property.
-#[derive(
- Clone,
- Debug,
- Eq,
- MallocSizeOf,
- PartialEq,
- ToComputedValue,
- SpecifiedValueInfo,
- ToCss,
- ToResolvedValue,
- ToShmem,
-)]
-#[repr(u8)]
-pub enum GenericContentItem<I> {
- /// Literal string content.
- String(crate::OwnedStr),
- /// `counter(name, style)`.
- #[css(comma, function)]
- Counter(CustomIdent, #[css(skip_if = "is_decimal")] CounterStyleType),
- /// `counters(name, separator, style)`.
- #[css(comma, function)]
- Counters(
- CustomIdent,
- crate::OwnedStr,
- #[css(skip_if = "is_decimal")] CounterStyleType,
- ),
- /// `open-quote`.
- OpenQuote,
- /// `close-quote`.
- CloseQuote,
- /// `no-open-quote`.
- NoOpenQuote,
- /// `no-close-quote`.
- NoCloseQuote,
- /// `-moz-alt-content`.
- #[cfg(feature = "gecko")]
- MozAltContent,
- /// `-moz-label-content`.
- /// This is needed to make `accesskey` work for XUL labels. It's basically
- /// attr(value) otherwise.
- #[cfg(feature = "gecko")]
- MozLabelContent,
- /// `attr([namespace? `|`]? ident)`
- Attr(Attr),
- /// image-set(url) | url(url)
- Image(I),
-}
-
-pub use self::GenericContentItem as ContentItem;
diff --git a/components/style/values/generics/easing.rs b/components/style/values/generics/easing.rs
deleted file mode 100644
index 803a07fada6..00000000000
--- a/components/style/values/generics/easing.rs
+++ /dev/null
@@ -1,135 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
-
-//! Generic types for CSS Easing Functions.
-//! https://drafts.csswg.org/css-easing/#timing-functions
-
-use crate::parser::ParserContext;
-
-/// A generic easing function.
-#[derive(
- Clone,
- Debug,
- MallocSizeOf,
- PartialEq,
- SpecifiedValueInfo,
- ToCss,
- ToShmem,
- Serialize,
- Deserialize,
-)]
-#[value_info(ty = "TIMING_FUNCTION")]
-#[repr(u8, C)]
-pub enum TimingFunction<Integer, Number, LinearStops> {
- /// `linear | ease | ease-in | ease-out | ease-in-out`
- Keyword(TimingKeyword),
- /// `cubic-bezier(<number>, <number>, <number>, <number>)`
- #[allow(missing_docs)]
- #[css(comma, function)]
- CubicBezier {
- x1: Number,
- y1: Number,
- x2: Number,
- y2: Number,
- },
- /// `step-start | step-end | steps(<integer>, [ <step-position> ]?)`
- /// `<step-position> = jump-start | jump-end | jump-none | jump-both | start | end`
- #[css(comma, function)]
- #[value_info(other_values = "step-start,step-end")]
- Steps(Integer, #[css(skip_if = "is_end")] StepPosition),
- /// linear([<linear-stop>]#)
- /// <linear-stop> = <output> && <linear-stop-length>?
- /// <linear-stop-length> = <percentage>{1, 2}
- #[css(function = "linear")]
- LinearFunction(LinearStops),
-}
-
-#[allow(missing_docs)]
-#[derive(
- Clone,
- Copy,
- Debug,
- Eq,
- MallocSizeOf,
- Parse,
- PartialEq,
- SpecifiedValueInfo,
- ToComputedValue,
- ToCss,
- ToResolvedValue,
- ToShmem,
- Serialize,
- Deserialize,
-)]
-#[repr(u8)]
-pub enum TimingKeyword {
- Linear,
- Ease,
- EaseIn,
- EaseOut,
- EaseInOut,
-}
-
-/// Before flag, defined as per https://drafts.csswg.org/css-easing/#before-flag
-/// This flag is never user-specified.
-#[allow(missing_docs)]
-#[derive(PartialEq)]
-#[repr(u8)]
-pub enum BeforeFlag {
- Unset,
- Set,
-}
-
-#[cfg(feature = "gecko")]
-fn step_position_jump_enabled(_context: &ParserContext) -> bool {
- static_prefs::pref!("layout.css.step-position-jump.enabled")
-}
-
-#[cfg(feature = "servo")]
-fn step_position_jump_enabled(_context: &ParserContext) -> bool {
- false
-}
-
-#[allow(missing_docs)]
-#[derive(
- Clone,
- Copy,
- Debug,
- Eq,
- MallocSizeOf,
- Parse,
- PartialEq,
- ToComputedValue,
- ToCss,
- ToResolvedValue,
- ToShmem,
- Serialize,
- Deserialize,
-)]
-#[repr(u8)]
-pub enum StepPosition {
- #[parse(condition = "step_position_jump_enabled")]
- JumpStart,
- #[parse(condition = "step_position_jump_enabled")]
- JumpEnd,
- #[parse(condition = "step_position_jump_enabled")]
- JumpNone,
- #[parse(condition = "step_position_jump_enabled")]
- JumpBoth,
- Start,
- End,
-}
-
-#[inline]
-fn is_end(position: &StepPosition) -> bool {
- *position == StepPosition::JumpEnd || *position == StepPosition::End
-}
-
-impl<Integer, Number, LinearStops> TimingFunction<Integer, Number, LinearStops> {
- /// `ease`
- #[inline]
- pub fn ease() -> Self {
- TimingFunction::Keyword(TimingKeyword::Ease)
- }
-}
diff --git a/components/style/values/generics/effects.rs b/components/style/values/generics/effects.rs
deleted file mode 100644
index f5666f30552..00000000000
--- a/components/style/values/generics/effects.rs
+++ /dev/null
@@ -1,121 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
-
-//! Generic types for CSS values related to effects.
-
-/// A generic value for a single `box-shadow`.
-#[derive(
- Animate,
- Clone,
- ComputeSquaredDistance,
- Debug,
- MallocSizeOf,
- PartialEq,
- SpecifiedValueInfo,
- ToAnimatedValue,
- ToAnimatedZero,
- ToCss,
- ToResolvedValue,
- ToShmem,
-)]
-#[repr(C)]
-pub struct GenericBoxShadow<Color, SizeLength, BlurShapeLength, ShapeLength> {
- /// The base shadow.
- pub base: GenericSimpleShadow<Color, SizeLength, BlurShapeLength>,
- /// The spread radius.
- pub spread: ShapeLength,
- /// Whether this is an inset box shadow.
- #[animation(constant)]
- #[css(represents_keyword)]
- pub inset: bool,
-}
-
-pub use self::GenericBoxShadow as BoxShadow;
-
-/// A generic value for a single `filter`.
-#[cfg_attr(feature = "servo", derive(Deserialize, Serialize))]
-#[derive(
- Clone,
- ComputeSquaredDistance,
- Debug,
- MallocSizeOf,
- PartialEq,
- SpecifiedValueInfo,
- ToAnimatedValue,
- ToComputedValue,
- ToCss,
- ToResolvedValue,
- ToShmem,
-)]
-#[animation(no_bound(U))]
-#[repr(C, u8)]
-pub enum GenericFilter<Angle, NonNegativeFactor, ZeroToOneFactor, Length, Shadow, U> {
- /// `blur(<length>)`
- #[css(function)]
- Blur(Length),
- /// `brightness(<factor>)`
- #[css(function)]
- Brightness(NonNegativeFactor),
- /// `contrast(<factor>)`
- #[css(function)]
- Contrast(NonNegativeFactor),
- /// `grayscale(<factor>)`
- #[css(function)]
- Grayscale(ZeroToOneFactor),
- /// `hue-rotate(<angle>)`
- #[css(function)]
- HueRotate(Angle),
- /// `invert(<factor>)`
- #[css(function)]
- Invert(ZeroToOneFactor),
- /// `opacity(<factor>)`
- #[css(function)]
- Opacity(ZeroToOneFactor),
- /// `saturate(<factor>)`
- #[css(function)]
- Saturate(NonNegativeFactor),
- /// `sepia(<factor>)`
- #[css(function)]
- Sepia(ZeroToOneFactor),
- /// `drop-shadow(...)`
- #[css(function)]
- DropShadow(Shadow),
- /// `<url>`
- #[animation(error)]
- Url(U),
-}
-
-pub use self::GenericFilter as Filter;
-
-/// A generic value for the `drop-shadow()` filter and the `text-shadow` property.
-///
-/// Contrary to the canonical order from the spec, the color is serialised
-/// first, like in Gecko and Webkit.
-#[derive(
- Animate,
- Clone,
- ComputeSquaredDistance,
- Debug,
- MallocSizeOf,
- PartialEq,
- SpecifiedValueInfo,
- ToAnimatedValue,
- ToAnimatedZero,
- ToCss,
- ToResolvedValue,
- ToShmem,
-)]
-#[repr(C)]
-pub struct GenericSimpleShadow<Color, SizeLength, ShapeLength> {
- /// Color.
- pub color: Color,
- /// Horizontal radius.
- pub horizontal: SizeLength,
- /// Vertical radius.
- pub vertical: SizeLength,
- /// Blur radius.
- pub blur: ShapeLength,
-}
-
-pub use self::GenericSimpleShadow as SimpleShadow;
diff --git a/components/style/values/generics/flex.rs b/components/style/values/generics/flex.rs
deleted file mode 100644
index 85b64000f2b..00000000000
--- a/components/style/values/generics/flex.rs
+++ /dev/null
@@ -1,33 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
-
-//! Generic types for CSS values related to flexbox.
-
-/// A generic value for the `flex-basis` property.
-#[derive(
- Animate,
- Clone,
- ComputeSquaredDistance,
- Copy,
- Debug,
- MallocSizeOf,
- Parse,
- PartialEq,
- SpecifiedValueInfo,
- ToAnimatedValue,
- ToAnimatedZero,
- ToComputedValue,
- ToCss,
- ToResolvedValue,
- ToShmem,
-)]
-#[repr(C)]
-pub enum GenericFlexBasis<S> {
- /// `content`
- Content,
- /// `<width>`
- Size(S),
-}
-
-pub use self::GenericFlexBasis as FlexBasis;
diff --git a/components/style/values/generics/font.rs b/components/style/values/generics/font.rs
deleted file mode 100644
index 09ed542e97a..00000000000
--- a/components/style/values/generics/font.rs
+++ /dev/null
@@ -1,271 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
-
-//! Generic types for font stuff.
-
-use crate::parser::{Parse, ParserContext};
-use crate::One;
-use byteorder::{BigEndian, ReadBytesExt};
-use cssparser::Parser;
-use std::fmt::{self, Write};
-use std::io::Cursor;
-use style_traits::{CssWriter, ParseError};
-use style_traits::{StyleParseErrorKind, ToCss};
-
-/// A trait for values that are labelled with a FontTag (for feature and
-/// variation settings).
-pub trait TaggedFontValue {
- /// The value's tag.
- fn tag(&self) -> FontTag;
-}
-
-/// https://drafts.csswg.org/css-fonts-4/#feature-tag-value
-#[derive(
- Clone,
- Debug,
- Eq,
- MallocSizeOf,
- PartialEq,
- SpecifiedValueInfo,
- ToComputedValue,
- ToResolvedValue,
- ToShmem,
-)]
-pub struct FeatureTagValue<Integer> {
- /// A four-character tag, packed into a u32 (one byte per character).
- pub tag: FontTag,
- /// The actual value.
- pub value: Integer,
-}
-
-impl<T> TaggedFontValue for FeatureTagValue<T> {
- fn tag(&self) -> FontTag {
- self.tag
- }
-}
-
-impl<Integer> ToCss for FeatureTagValue<Integer>
-where
- Integer: One + ToCss + PartialEq,
-{
- fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
- where
- W: Write,
- {
- self.tag.to_css(dest)?;
- // Don't serialize the default value.
- if !self.value.is_one() {
- dest.write_char(' ')?;
- self.value.to_css(dest)?;
- }
-
- Ok(())
- }
-}
-
-/// Variation setting for a single feature, see:
-///
-/// https://drafts.csswg.org/css-fonts-4/#font-variation-settings-def
-#[derive(
- Animate,
- Clone,
- ComputeSquaredDistance,
- Debug,
- Eq,
- MallocSizeOf,
- PartialEq,
- SpecifiedValueInfo,
- ToComputedValue,
- ToCss,
- ToResolvedValue,
- ToShmem,
-)]
-pub struct VariationValue<Number> {
- /// A four-character tag, packed into a u32 (one byte per character).
- #[animation(constant)]
- pub tag: FontTag,
- /// The actual value.
- pub value: Number,
-}
-
-impl<T> TaggedFontValue for VariationValue<T> {
- fn tag(&self) -> FontTag {
- self.tag
- }
-}
-
-/// A value both for font-variation-settings and font-feature-settings.
-#[derive(
- Clone, Debug, Eq, MallocSizeOf, PartialEq, SpecifiedValueInfo, ToCss, ToResolvedValue, ToShmem,
-)]
-#[css(comma)]
-pub struct FontSettings<T>(#[css(if_empty = "normal", iterable)] pub Box<[T]>);
-
-impl<T> FontSettings<T> {
- /// Default value of font settings as `normal`.
- #[inline]
- pub fn normal() -> Self {
- FontSettings(vec![].into_boxed_slice())
- }
-}
-
-impl<T: Parse> Parse for FontSettings<T> {
- /// https://drafts.csswg.org/css-fonts-4/#descdef-font-face-font-feature-settings
- /// https://drafts.csswg.org/css-fonts-4/#font-variation-settings-def
- fn parse<'i, 't>(
- context: &ParserContext,
- input: &mut Parser<'i, 't>,
- ) -> Result<Self, ParseError<'i>> {
- if input
- .try_parse(|i| i.expect_ident_matching("normal"))
- .is_ok()
- {
- return Ok(Self::normal());
- }
-
- Ok(FontSettings(
- input
- .parse_comma_separated(|i| T::parse(context, i))?
- .into_boxed_slice(),
- ))
- }
-}
-
-/// A font four-character tag, represented as a u32 for convenience.
-///
-/// See:
-/// https://drafts.csswg.org/css-fonts-4/#font-variation-settings-def
-/// https://drafts.csswg.org/css-fonts-4/#descdef-font-face-font-feature-settings
-///
-#[derive(
- Clone,
- Copy,
- Debug,
- Eq,
- MallocSizeOf,
- PartialEq,
- SpecifiedValueInfo,
- ToComputedValue,
- ToResolvedValue,
- ToShmem,
-)]
-pub struct FontTag(pub u32);
-
-impl ToCss for FontTag {
- fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
- where
- W: Write,
- {
- use byteorder::ByteOrder;
- use std::str;
-
- let mut raw = [0u8; 4];
- BigEndian::write_u32(&mut raw, self.0);
- str::from_utf8(&raw).unwrap_or_default().to_css(dest)
- }
-}
-
-impl Parse for FontTag {
- fn parse<'i, 't>(
- _context: &ParserContext,
- input: &mut Parser<'i, 't>,
- ) -> Result<Self, ParseError<'i>> {
- let location = input.current_source_location();
- let tag = input.expect_string()?;
-
- // allowed strings of length 4 containing chars: <U+20, U+7E>
- if tag.len() != 4 || tag.as_bytes().iter().any(|c| *c < b' ' || *c > b'~') {
- return Err(location.new_custom_error(StyleParseErrorKind::UnspecifiedError));
- }
-
- let mut raw = Cursor::new(tag.as_bytes());
- Ok(FontTag(raw.read_u32::<BigEndian>().unwrap()))
- }
-}
-
-/// A generic value for the `font-style` property.
-///
-/// https://drafts.csswg.org/css-fonts-4/#font-style-prop
-#[allow(missing_docs)]
-#[cfg_attr(feature = "servo", derive(Deserialize, Serialize))]
-#[derive(
- Animate,
- Clone,
- ComputeSquaredDistance,
- Copy,
- Debug,
- Hash,
- MallocSizeOf,
- PartialEq,
- SpecifiedValueInfo,
- ToAnimatedValue,
- ToAnimatedZero,
- ToResolvedValue,
- ToShmem,
-)]
-pub enum FontStyle<Angle> {
- #[animation(error)]
- Normal,
- #[animation(error)]
- Italic,
- #[value_info(starts_with_keyword)]
- Oblique(Angle),
-}
-
-/// A generic value for the `font-size-adjust` property.
-///
-/// https://www.w3.org/TR/css-fonts-4/#font-size-adjust-prop
-/// https://github.com/w3c/csswg-drafts/issues/6160
-/// https://github.com/w3c/csswg-drafts/issues/6288
-#[allow(missing_docs)]
-#[repr(u8)]
-#[derive(
- Animate,
- Clone,
- ComputeSquaredDistance,
- Copy,
- Debug,
- Hash,
- MallocSizeOf,
- PartialEq,
- SpecifiedValueInfo,
- ToAnimatedValue,
- ToAnimatedZero,
- ToComputedValue,
- ToResolvedValue,
- ToShmem,
-)]
-pub enum GenericFontSizeAdjust<Number> {
- #[animation(error)]
- None,
- // 'ex-height' is the implied basis, so the keyword can be omitted
- ExHeight(Number),
- #[value_info(starts_with_keyword)]
- CapHeight(Number),
- #[value_info(starts_with_keyword)]
- ChWidth(Number),
- #[value_info(starts_with_keyword)]
- IcWidth(Number),
- #[value_info(starts_with_keyword)]
- IcHeight(Number),
-}
-
-impl<Number: ToCss> ToCss for GenericFontSizeAdjust<Number> {
- fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
- where
- W: Write,
- {
- let (prefix, value) = match self {
- Self::None => return dest.write_str("none"),
- Self::ExHeight(v) => ("", v),
- Self::CapHeight(v) => ("cap-height ", v),
- Self::ChWidth(v) => ("ch-width ", v),
- Self::IcWidth(v) => ("ic-width ", v),
- Self::IcHeight(v) => ("ic-height ", v),
- };
-
- dest.write_str(prefix)?;
- value.to_css(dest)
- }
-}
diff --git a/components/style/values/generics/grid.rs b/components/style/values/generics/grid.rs
deleted file mode 100644
index e35c96a28ca..00000000000
--- a/components/style/values/generics/grid.rs
+++ /dev/null
@@ -1,829 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
-
-//! Generic types for the handling of
-//! [grids](https://drafts.csswg.org/css-grid/).
-
-use crate::parser::{Parse, ParserContext};
-use crate::values::specified;
-use crate::values::specified::grid::parse_line_names;
-use crate::values::{CSSFloat, CustomIdent};
-use crate::{Atom, Zero};
-use cssparser::Parser;
-use std::fmt::{self, Write};
-use std::{cmp, usize};
-use style_traits::{CssWriter, ParseError, StyleParseErrorKind, ToCss};
-
-/// These are the limits that we choose to clamp grid line numbers to.
-/// http://drafts.csswg.org/css-grid/#overlarge-grids
-/// line_num is clamped to this range at parse time.
-pub const MIN_GRID_LINE: i32 = -10000;
-/// See above.
-pub const MAX_GRID_LINE: i32 = 10000;
-
-/// A `<grid-line>` type.
-///
-/// <https://drafts.csswg.org/css-grid/#typedef-grid-row-start-grid-line>
-#[derive(
- Clone,
- Debug,
- Default,
- MallocSizeOf,
- PartialEq,
- SpecifiedValueInfo,
- ToComputedValue,
- ToResolvedValue,
- ToShmem,
-)]
-#[repr(C)]
-pub struct GenericGridLine<Integer> {
- /// A custom identifier for named lines, or the empty atom otherwise.
- ///
- /// <https://drafts.csswg.org/css-grid/#grid-placement-slot>
- pub ident: Atom,
- /// Denotes the nth grid line from grid item's placement.
- ///
- /// This is clamped by MIN_GRID_LINE and MAX_GRID_LINE.
- ///
- /// NOTE(emilio): If we ever allow animating these we need to either do
- /// something more complicated for the clamping, or do this clamping at
- /// used-value time.
- pub line_num: Integer,
- /// Flag to check whether it's a `span` keyword.
- pub is_span: bool,
-}
-
-pub use self::GenericGridLine as GridLine;
-
-impl<Integer> GridLine<Integer>
-where
- Integer: PartialEq + Zero,
-{
- /// The `auto` value.
- pub fn auto() -> Self {
- Self {
- is_span: false,
- line_num: Zero::zero(),
- ident: atom!(""),
- }
- }
-
- /// Check whether this `<grid-line>` represents an `auto` value.
- pub fn is_auto(&self) -> bool {
- self.ident == atom!("") && self.line_num.is_zero() && !self.is_span
- }
-
- /// Check whether this `<grid-line>` represents a `<custom-ident>` value.
- pub fn is_ident_only(&self) -> bool {
- self.ident != atom!("") && self.line_num.is_zero() && !self.is_span
- }
-
- /// Check if `self` makes `other` omittable according to the rules at:
- /// https://drafts.csswg.org/css-grid/#propdef-grid-column
- /// https://drafts.csswg.org/css-grid/#propdef-grid-area
- pub fn can_omit(&self, other: &Self) -> bool {
- if self.is_ident_only() {
- self == other
- } else {
- other.is_auto()
- }
- }
-}
-
-impl<Integer> ToCss for GridLine<Integer>
-where
- Integer: ToCss + PartialEq + Zero,
-{
- fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
- where
- W: Write,
- {
- if self.is_auto() {
- return dest.write_str("auto");
- }
-
- if self.is_span {
- dest.write_str("span")?;
- }
-
- if !self.line_num.is_zero() {
- if self.is_span {
- dest.write_char(' ')?;
- }
- self.line_num.to_css(dest)?;
- }
-
- if self.ident != atom!("") {
- if self.is_span || !self.line_num.is_zero() {
- dest.write_char(' ')?;
- }
- CustomIdent(self.ident.clone()).to_css(dest)?;
- }
-
- Ok(())
- }
-}
-
-impl Parse for GridLine<specified::Integer> {
- fn parse<'i, 't>(
- context: &ParserContext,
- input: &mut Parser<'i, 't>,
- ) -> Result<Self, ParseError<'i>> {
- let mut grid_line = Self::auto();
- if input.try_parse(|i| i.expect_ident_matching("auto")).is_ok() {
- return Ok(grid_line);
- }
-
- // <custom-ident> | [ <integer> && <custom-ident>? ] | [ span && [ <integer> || <custom-ident> ] ]
- // This <grid-line> horror is simply,
- // [ span? && [ <custom-ident> || <integer> ] ]
- // And, for some magical reason, "span" should be the first or last value and not in-between.
- let mut val_before_span = false;
-
- for _ in 0..3 {
- // Maximum possible entities for <grid-line>
- let location = input.current_source_location();
- if input.try_parse(|i| i.expect_ident_matching("span")).is_ok() {
- if grid_line.is_span {
- return Err(location.new_custom_error(StyleParseErrorKind::UnspecifiedError));
- }
-
- if !grid_line.line_num.is_zero() || grid_line.ident != atom!("") {
- val_before_span = true;
- }
-
- grid_line.is_span = true;
- } else if let Ok(i) = input.try_parse(|i| specified::Integer::parse(context, i)) {
- // FIXME(emilio): Probably shouldn't reject if it's calc()...
- let value = i.value();
- if value == 0 || val_before_span || !grid_line.line_num.is_zero() {
- return Err(location.new_custom_error(StyleParseErrorKind::UnspecifiedError));
- }
-
- grid_line.line_num = specified::Integer::new(cmp::max(
- MIN_GRID_LINE,
- cmp::min(value, MAX_GRID_LINE),
- ));
- } else if let Ok(name) = input.try_parse(|i| i.expect_ident_cloned()) {
- if val_before_span || grid_line.ident != atom!("") {
- return Err(location.new_custom_error(StyleParseErrorKind::UnspecifiedError));
- }
- // NOTE(emilio): `span` is consumed above, so we only need to
- // reject `auto`.
- grid_line.ident = CustomIdent::from_ident(location, &name, &["auto"])?.0;
- } else {
- break;
- }
- }
-
- if grid_line.is_auto() {
- return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError));
- }
-
- if grid_line.is_span {
- if !grid_line.line_num.is_zero() {
- if grid_line.line_num.value() <= 0 {
- // disallow negative integers for grid spans
- return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError));
- }
- } else if grid_line.ident == atom!("") {
- // integer could be omitted
- return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError));
- }
- }
-
- Ok(grid_line)
- }
-}
-
-/// A track breadth for explicit grid track sizing. It's generic solely to
-/// avoid re-implementing it for the computed type.
-///
-/// <https://drafts.csswg.org/css-grid/#typedef-track-breadth>
-#[derive(
- Animate,
- Clone,
- Debug,
- MallocSizeOf,
- PartialEq,
- SpecifiedValueInfo,
- ToComputedValue,
- ToCss,
- ToResolvedValue,
- ToShmem,
-)]
-#[repr(C, u8)]
-pub enum GenericTrackBreadth<L> {
- /// The generic type is almost always a non-negative `<length-percentage>`
- Breadth(L),
- /// A flex fraction specified in `fr` units.
- #[css(dimension)]
- Fr(CSSFloat),
- /// `auto`
- Auto,
- /// `min-content`
- MinContent,
- /// `max-content`
- MaxContent,
-}
-
-pub use self::GenericTrackBreadth as TrackBreadth;
-
-impl<L> TrackBreadth<L> {
- /// Check whether this is a `<fixed-breadth>` (i.e., it only has `<length-percentage>`)
- ///
- /// <https://drafts.csswg.org/css-grid/#typedef-fixed-breadth>
- #[inline]
- pub fn is_fixed(&self) -> bool {
- matches!(*self, TrackBreadth::Breadth(..))
- }
-}
-
-/// A `<track-size>` type for explicit grid track sizing. Like `<track-breadth>`, this is
-/// generic only to avoid code bloat. It only takes `<length-percentage>`
-///
-/// <https://drafts.csswg.org/css-grid/#typedef-track-size>
-#[derive(
- Clone,
- Debug,
- MallocSizeOf,
- PartialEq,
- SpecifiedValueInfo,
- ToComputedValue,
- ToResolvedValue,
- ToShmem,
-)]
-#[repr(C, u8)]
-pub enum GenericTrackSize<L> {
- /// A flexible `<track-breadth>`
- Breadth(GenericTrackBreadth<L>),
- /// A `minmax` function for a range over an inflexible `<track-breadth>`
- /// and a flexible `<track-breadth>`
- ///
- /// <https://drafts.csswg.org/css-grid/#valdef-grid-template-columns-minmax>
- #[css(function)]
- Minmax(GenericTrackBreadth<L>, GenericTrackBreadth<L>),
- /// A `fit-content` function.
- ///
- /// This stores a TrackBreadth<L> for convenience, but it can only be a
- /// LengthPercentage.
- ///
- /// <https://drafts.csswg.org/css-grid/#valdef-grid-template-columns-fit-content>
- #[css(function)]
- FitContent(GenericTrackBreadth<L>),
-}
-
-pub use self::GenericTrackSize as TrackSize;
-
-impl<L> TrackSize<L> {
- /// The initial value.
- const INITIAL_VALUE: Self = TrackSize::Breadth(TrackBreadth::Auto);
-
- /// Returns the initial value.
- pub const fn initial_value() -> Self {
- Self::INITIAL_VALUE
- }
-
- /// Returns true if `self` is the initial value.
- pub fn is_initial(&self) -> bool {
- matches!(*self, TrackSize::Breadth(TrackBreadth::Auto)) // FIXME: can't use Self::INITIAL_VALUE here yet: https://github.com/rust-lang/rust/issues/66585
- }
-
- /// Check whether this is a `<fixed-size>`
- ///
- /// <https://drafts.csswg.org/css-grid/#typedef-fixed-size>
- pub fn is_fixed(&self) -> bool {
- match *self {
- TrackSize::Breadth(ref breadth) => breadth.is_fixed(),
- // For minmax function, it could be either
- // minmax(<fixed-breadth>, <track-breadth>) or minmax(<inflexible-breadth>, <fixed-breadth>),
- // and since both variants are a subset of minmax(<inflexible-breadth>, <track-breadth>), we only
- // need to make sure that they're fixed. So, we don't have to modify the parsing function.
- TrackSize::Minmax(ref breadth_1, ref breadth_2) => {
- if breadth_1.is_fixed() {
- return true; // the second value is always a <track-breadth>
- }
-
- match *breadth_1 {
- TrackBreadth::Fr(_) => false, // should be <inflexible-breadth> at this point
- _ => breadth_2.is_fixed(),
- }
- },
- TrackSize::FitContent(_) => false,
- }
- }
-}
-
-impl<L> Default for TrackSize<L> {
- fn default() -> Self {
- Self::initial_value()
- }
-}
-
-impl<L: ToCss> ToCss for TrackSize<L> {
- fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
- where
- W: Write,
- {
- match *self {
- TrackSize::Breadth(ref breadth) => breadth.to_css(dest),
- TrackSize::Minmax(ref min, ref max) => {
- // According to gecko minmax(auto, <flex>) is equivalent to <flex>,
- // and both are serialized as <flex>.
- if let TrackBreadth::Auto = *min {
- if let TrackBreadth::Fr(_) = *max {
- return max.to_css(dest);
- }
- }
-
- dest.write_str("minmax(")?;
- min.to_css(dest)?;
- dest.write_str(", ")?;
- max.to_css(dest)?;
- dest.write_char(')')
- },
- TrackSize::FitContent(ref lp) => {
- dest.write_str("fit-content(")?;
- lp.to_css(dest)?;
- dest.write_char(')')
- },
- }
- }
-}
-
-/// A `<track-size>+`.
-/// We use the empty slice as `auto`, and always parse `auto` as an empty slice.
-/// This means it's impossible to have a slice containing only one auto item.
-#[derive(
- Clone,
- Debug,
- Default,
- MallocSizeOf,
- PartialEq,
- SpecifiedValueInfo,
- ToComputedValue,
- ToCss,
- ToResolvedValue,
- ToShmem,
-)]
-#[repr(transparent)]
-pub struct GenericImplicitGridTracks<T>(
- #[css(if_empty = "auto", iterable)] pub crate::OwnedSlice<T>,
-);
-
-pub use self::GenericImplicitGridTracks as ImplicitGridTracks;
-
-impl<T: fmt::Debug + Default + PartialEq> ImplicitGridTracks<T> {
- /// Returns true if current value is same as its initial value (i.e. auto).
- pub fn is_initial(&self) -> bool {
- debug_assert_ne!(
- *self,
- ImplicitGridTracks(crate::OwnedSlice::from(vec![Default::default()]))
- );
- self.0.is_empty()
- }
-}
-
-/// Helper function for serializing identifiers with a prefix and suffix, used
-/// for serializing <line-names> (in grid).
-pub fn concat_serialize_idents<W>(
- prefix: &str,
- suffix: &str,
- slice: &[CustomIdent],
- sep: &str,
- dest: &mut CssWriter<W>,
-) -> fmt::Result
-where
- W: Write,
-{
- if let Some((ref first, rest)) = slice.split_first() {
- dest.write_str(prefix)?;
- first.to_css(dest)?;
- for thing in rest {
- dest.write_str(sep)?;
- thing.to_css(dest)?;
- }
-
- dest.write_str(suffix)?;
- }
-
- Ok(())
-}
-
-/// The initial argument of the `repeat` function.
-///
-/// <https://drafts.csswg.org/css-grid/#typedef-track-repeat>
-#[derive(
- Clone, Copy, Debug, MallocSizeOf, PartialEq, ToComputedValue, ToCss, ToResolvedValue, ToShmem,
-)]
-#[repr(C, u8)]
-pub enum RepeatCount<Integer> {
- /// A positive integer. This is allowed only for `<track-repeat>` and `<fixed-repeat>`
- Number(Integer),
- /// An `<auto-fill>` keyword allowed only for `<auto-repeat>`
- AutoFill,
- /// An `<auto-fit>` keyword allowed only for `<auto-repeat>`
- AutoFit,
-}
-
-impl Parse for RepeatCount<specified::Integer> {
- fn parse<'i, 't>(
- context: &ParserContext,
- input: &mut Parser<'i, 't>,
- ) -> Result<Self, ParseError<'i>> {
- if let Ok(mut i) = input.try_parse(|i| specified::Integer::parse_positive(context, i)) {
- if i.value() > MAX_GRID_LINE {
- i = specified::Integer::new(MAX_GRID_LINE);
- }
- return Ok(RepeatCount::Number(i));
- }
- try_match_ident_ignore_ascii_case! { input,
- "auto-fill" => Ok(RepeatCount::AutoFill),
- "auto-fit" => Ok(RepeatCount::AutoFit),
- }
- }
-}
-
-/// The structure containing `<line-names>` and `<track-size>` values.
-///
-/// It can also hold `repeat()` function parameters, which expands into the respective
-/// values in its computed form.
-#[derive(
- Clone,
- Debug,
- MallocSizeOf,
- PartialEq,
- SpecifiedValueInfo,
- ToComputedValue,
- ToResolvedValue,
- ToShmem,
-)]
-#[css(function = "repeat")]
-#[repr(C)]
-pub struct GenericTrackRepeat<L, I> {
- /// The number of times for the value to be repeated (could also be `auto-fit` or `auto-fill`)
- pub count: RepeatCount<I>,
- /// `<line-names>` accompanying `<track_size>` values.
- ///
- /// If there's no `<line-names>`, then it's represented by an empty vector.
- /// For N `<track-size>` values, there will be N+1 `<line-names>`, and so this vector's
- /// length is always one value more than that of the `<track-size>`.
- pub line_names: crate::OwnedSlice<crate::OwnedSlice<CustomIdent>>,
- /// `<track-size>` values.
- pub track_sizes: crate::OwnedSlice<GenericTrackSize<L>>,
-}
-
-pub use self::GenericTrackRepeat as TrackRepeat;
-
-impl<L: ToCss, I: ToCss> ToCss for TrackRepeat<L, I> {
- fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
- where
- W: Write,
- {
- dest.write_str("repeat(")?;
- self.count.to_css(dest)?;
- dest.write_str(", ")?;
-
- let mut line_names_iter = self.line_names.iter();
- for (i, (ref size, ref names)) in self
- .track_sizes
- .iter()
- .zip(&mut line_names_iter)
- .enumerate()
- {
- if i > 0 {
- dest.write_char(' ')?;
- }
-
- concat_serialize_idents("[", "] ", names, " ", dest)?;
- size.to_css(dest)?;
- }
-
- if let Some(line_names_last) = line_names_iter.next() {
- concat_serialize_idents(" [", "]", line_names_last, " ", dest)?;
- }
-
- dest.write_char(')')?;
-
- Ok(())
- }
-}
-
-/// Track list values. Can be <track-size> or <track-repeat>
-#[derive(
- Animate,
- Clone,
- Debug,
- MallocSizeOf,
- PartialEq,
- SpecifiedValueInfo,
- ToComputedValue,
- ToCss,
- ToResolvedValue,
- ToShmem,
-)]
-#[repr(C, u8)]
-pub enum GenericTrackListValue<LengthPercentage, Integer> {
- /// A <track-size> value.
- TrackSize(#[animation(field_bound)] GenericTrackSize<LengthPercentage>),
- /// A <track-repeat> value.
- TrackRepeat(#[animation(field_bound)] GenericTrackRepeat<LengthPercentage, Integer>),
-}
-
-pub use self::GenericTrackListValue as TrackListValue;
-
-impl<L, I> TrackListValue<L, I> {
- // FIXME: can't use TrackSize::initial_value() here b/c rustc error "is not yet stable as a const fn"
- const INITIAL_VALUE: Self = TrackListValue::TrackSize(TrackSize::Breadth(TrackBreadth::Auto));
-
- fn is_repeat(&self) -> bool {
- matches!(*self, TrackListValue::TrackRepeat(..))
- }
-
- /// Returns true if `self` is the initial value.
- pub fn is_initial(&self) -> bool {
- matches!(
- *self,
- TrackListValue::TrackSize(TrackSize::Breadth(TrackBreadth::Auto))
- ) // FIXME: can't use Self::INITIAL_VALUE here yet: https://github.com/rust-lang/rust/issues/66585
- }
-}
-
-impl<L, I> Default for TrackListValue<L, I> {
- #[inline]
- fn default() -> Self {
- Self::INITIAL_VALUE
- }
-}
-
-/// A grid `<track-list>` type.
-///
-/// <https://drafts.csswg.org/css-grid/#typedef-track-list>
-#[derive(
- Clone,
- Debug,
- MallocSizeOf,
- PartialEq,
- SpecifiedValueInfo,
- ToComputedValue,
- ToResolvedValue,
- ToShmem,
-)]
-#[repr(C)]
-pub struct GenericTrackList<LengthPercentage, Integer> {
- /// The index in `values` where our `<auto-repeat>` value is, if in bounds.
- #[css(skip)]
- pub auto_repeat_index: usize,
- /// A vector of `<track-size> | <track-repeat>` values.
- pub values: crate::OwnedSlice<GenericTrackListValue<LengthPercentage, Integer>>,
- /// `<line-names>` accompanying `<track-size> | <track-repeat>` values.
- ///
- /// If there's no `<line-names>`, then it's represented by an empty vector.
- /// For N values, there will be N+1 `<line-names>`, and so this vector's
- /// length is always one value more than that of the `<track-size>`.
- pub line_names: crate::OwnedSlice<crate::OwnedSlice<CustomIdent>>,
-}
-
-pub use self::GenericTrackList as TrackList;
-
-impl<L, I> TrackList<L, I> {
- /// Whether this track list is an explicit track list (that is, doesn't have
- /// any repeat values).
- pub fn is_explicit(&self) -> bool {
- !self.values.iter().any(|v| v.is_repeat())
- }
-
- /// Whether this track list has an `<auto-repeat>` value.
- pub fn has_auto_repeat(&self) -> bool {
- self.auto_repeat_index < self.values.len()
- }
-}
-
-impl<L: ToCss, I: ToCss> ToCss for TrackList<L, I> {
- fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
- where
- W: Write,
- {
- let mut values_iter = self.values.iter().peekable();
- let mut line_names_iter = self.line_names.iter().peekable();
-
- for idx in 0.. {
- let names = line_names_iter.next().unwrap(); // This should exist!
- concat_serialize_idents("[", "]", names, " ", dest)?;
-
- match values_iter.next() {
- Some(value) => {
- if !names.is_empty() {
- dest.write_char(' ')?;
- }
-
- value.to_css(dest)?;
- },
- None => break,
- }
-
- if values_iter.peek().is_some() ||
- line_names_iter.peek().map_or(false, |v| !v.is_empty()) ||
- (idx + 1 == self.auto_repeat_index)
- {
- dest.write_char(' ')?;
- }
- }
-
- Ok(())
- }
-}
-
-/// The `<line-name-list>` for subgrids.
-///
-/// `subgrid [ <line-names> | repeat(<positive-integer> | auto-fill, <line-names>+) ]+`
-///
-/// https://drafts.csswg.org/css-grid-2/#typedef-line-name-list
-#[derive(
- Clone,
- Debug,
- Default,
- MallocSizeOf,
- PartialEq,
- SpecifiedValueInfo,
- ToComputedValue,
- ToResolvedValue,
- ToShmem,
-)]
-#[repr(C)]
-pub struct LineNameList {
- /// The optional `<line-name-list>`
- pub names: crate::OwnedSlice<crate::OwnedSlice<CustomIdent>>,
- /// Indicates the starting line names that requires `auto-fill`, if in bounds.
- pub fill_start: usize,
- /// Indicates the number of line names in the auto-fill
- pub fill_len: usize,
-}
-
-impl Parse for LineNameList {
- fn parse<'i, 't>(
- context: &ParserContext,
- input: &mut Parser<'i, 't>,
- ) -> Result<Self, ParseError<'i>> {
- input.expect_ident_matching("subgrid")?;
- let mut line_names = vec![];
- let mut fill_data = None;
- // Rather than truncating the result after inserting values, just
- // have a maximum number of values. This gives us an early out on very
- // large name lists, but more importantly prevents OOM on huge repeat
- // expansions. (bug 1583429)
- let mut max_remaining = MAX_GRID_LINE as usize;
-
- loop {
- let repeat_parse_result = input.try_parse(|input| {
- input.expect_function_matching("repeat")?;
- input.parse_nested_block(|input| {
- let count = RepeatCount::parse(context, input)?;
- input.expect_comma()?;
- let mut names_list = vec![];
- names_list.push(parse_line_names(input)?); // there should be at least one
- while let Ok(names) = input.try_parse(parse_line_names) {
- names_list.push(names);
- }
- Ok((names_list, count))
- })
- });
- if let Ok((names_list, count)) = repeat_parse_result {
- let mut handle_size = |n| {
- let n = cmp::min(n, max_remaining);
- max_remaining -= n;
- n
- };
- match count {
- // FIXME(emilio): we shouldn't expand repeat() at
- // parse time for subgrid. (bug 1583429)
- RepeatCount::Number(num) => {
- let n = handle_size(num.value() as usize * names_list.len());
- line_names.extend(names_list.iter().cloned().cycle().take(n));
- },
- RepeatCount::AutoFill if fill_data.is_none() => {
- let fill_idx = line_names.len();
- let fill_len = names_list.len();
- fill_data = Some((fill_idx, fill_len));
- let n = handle_size(fill_len);
- line_names.extend(names_list.into_iter().take(n));
- },
- _ => return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError)),
- }
- } else if let Ok(names) = input.try_parse(parse_line_names) {
- if max_remaining > 0 {
- line_names.push(names);
- max_remaining -= 1;
- }
- } else {
- break;
- }
- }
-
- debug_assert!(line_names.len() <= MAX_GRID_LINE as usize);
-
- let (fill_start, fill_len) = fill_data.unwrap_or((0, 0));
-
- Ok(LineNameList {
- names: line_names.into(),
- fill_start: fill_start,
- fill_len: fill_len,
- })
- }
-}
-
-impl ToCss for LineNameList {
- fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
- where
- W: Write,
- {
- dest.write_str("subgrid")?;
- let fill_start = self.fill_start;
- let fill_len = self.fill_len;
- for (i, names) in self.names.iter().enumerate() {
- if fill_len > 0 && i == fill_start {
- dest.write_str(" repeat(auto-fill,")?;
- }
-
- dest.write_str(" [")?;
-
- if let Some((ref first, rest)) = names.split_first() {
- first.to_css(dest)?;
- for name in rest {
- dest.write_char(' ')?;
- name.to_css(dest)?;
- }
- }
-
- dest.write_char(']')?;
- if fill_len > 0 && i == fill_start + fill_len - 1 {
- dest.write_char(')')?;
- }
- }
-
- Ok(())
- }
-}
-
-/// Variants for `<grid-template-rows> | <grid-template-columns>`
-#[derive(
- Animate,
- Clone,
- Debug,
- MallocSizeOf,
- PartialEq,
- SpecifiedValueInfo,
- ToComputedValue,
- ToCss,
- ToResolvedValue,
- ToShmem,
-)]
-#[value_info(other_values = "subgrid")]
-#[repr(C, u8)]
-pub enum GenericGridTemplateComponent<L, I> {
- /// `none` value.
- None,
- /// The grid `<track-list>`
- TrackList(
- #[animation(field_bound)]
- #[compute(field_bound)]
- #[resolve(field_bound)]
- #[shmem(field_bound)]
- Box<GenericTrackList<L, I>>,
- ),
- /// A `subgrid <line-name-list>?`
- /// TODO: Support animations for this after subgrid is addressed in [grid-2] spec.
- #[animation(error)]
- Subgrid(Box<LineNameList>),
- /// `masonry` value.
- /// https://github.com/w3c/csswg-drafts/issues/4650
- Masonry,
-}
-
-pub use self::GenericGridTemplateComponent as GridTemplateComponent;
-
-impl<L, I> GridTemplateComponent<L, I> {
- /// The initial value.
- const INITIAL_VALUE: Self = Self::None;
-
- /// Returns length of the <track-list>s <track-size>
- pub fn track_list_len(&self) -> usize {
- match *self {
- GridTemplateComponent::TrackList(ref tracklist) => tracklist.values.len(),
- _ => 0,
- }
- }
-
- /// Returns true if `self` is the initial value.
- pub fn is_initial(&self) -> bool {
- matches!(*self, Self::None) // FIXME: can't use Self::INITIAL_VALUE here yet: https://github.com/rust-lang/rust/issues/66585
- }
-}
-
-impl<L, I> Default for GridTemplateComponent<L, I> {
- #[inline]
- fn default() -> Self {
- Self::INITIAL_VALUE
- }
-}
diff --git a/components/style/values/generics/image.rs b/components/style/values/generics/image.rs
deleted file mode 100644
index 7e0ac7ec5b2..00000000000
--- a/components/style/values/generics/image.rs
+++ /dev/null
@@ -1,614 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
-
-//! Generic types for the handling of [images].
-//!
-//! [images]: https://drafts.csswg.org/css-images/#image-values
-
-use crate::custom_properties;
-use crate::values::generics::position::PositionComponent;
-use crate::values::generics::Optional;
-use crate::values::serialize_atom_identifier;
-use crate::Atom;
-use crate::Zero;
-use servo_arc::Arc;
-use std::fmt::{self, Write};
-use style_traits::{CssWriter, ToCss};
-
-/// An `<image> | none` value.
-///
-/// https://drafts.csswg.org/css-images/#image-values
-#[derive(
- Clone, MallocSizeOf, PartialEq, SpecifiedValueInfo, ToComputedValue, ToResolvedValue, ToShmem,
-)]
-#[repr(C, u8)]
-pub enum GenericImage<G, MozImageRect, ImageUrl, Color, Percentage, Resolution> {
- /// `none` variant.
- None,
- /// A `<url()>` image.
- Url(ImageUrl),
-
- /// A `<gradient>` image. Gradients are rather large, and not nearly as
- /// common as urls, so we box them here to keep the size of this enum sane.
- Gradient(Box<G>),
- /// A `-moz-image-rect` image. Also fairly large and rare.
- // not cfg’ed out on non-Gecko to avoid `error[E0392]: parameter `MozImageRect` is never used`
- // Instead we make MozImageRect an empty enum
- Rect(Box<MozImageRect>),
-
- /// A `-moz-element(# <element-id>)`
- #[cfg(feature = "gecko")]
- #[css(function = "-moz-element")]
- Element(Atom),
-
- /// A paint worklet image.
- /// <https://drafts.css-houdini.org/css-paint-api/>
- #[cfg(feature = "servo")]
- PaintWorklet(PaintWorklet),
-
- /// A `<cross-fade()>` image. Storing this directly inside of
- /// GenericImage increases the size by 8 bytes so we box it here
- /// and store images directly inside of cross-fade instead of
- /// boxing them there.
- CrossFade(Box<GenericCrossFade<Self, Color, Percentage>>),
-
- /// An `image-set()` function.
- ImageSet(#[compute(field_bound)] Box<GenericImageSet<Self, Resolution>>),
-}
-
-pub use self::GenericImage as Image;
-
-/// <https://drafts.csswg.org/css-images-4/#cross-fade-function>
-#[derive(
- Clone, Debug, MallocSizeOf, PartialEq, ToResolvedValue, ToShmem, ToCss, ToComputedValue,
-)]
-#[css(comma, function = "cross-fade")]
-#[repr(C)]
-pub struct GenericCrossFade<Image, Color, Percentage> {
- /// All of the image percent pairings passed as arguments to
- /// cross-fade.
- #[css(iterable)]
- pub elements: crate::OwnedSlice<GenericCrossFadeElement<Image, Color, Percentage>>,
-}
-
-/// An optional percent and a cross fade image.
-#[derive(
- Clone, Debug, MallocSizeOf, PartialEq, ToComputedValue, ToResolvedValue, ToShmem, ToCss,
-)]
-#[repr(C)]
-pub struct GenericCrossFadeElement<Image, Color, Percentage> {
- /// The percent of the final image that `image` will be.
- pub percent: Optional<Percentage>,
- /// A color or image that will be blended when cross-fade is
- /// evaluated.
- pub image: GenericCrossFadeImage<Image, Color>,
-}
-
-/// An image or a color. `cross-fade` takes either when blending
-/// images together.
-#[derive(
- Clone, Debug, MallocSizeOf, PartialEq, ToComputedValue, ToResolvedValue, ToShmem, ToCss,
-)]
-#[repr(C, u8)]
-pub enum GenericCrossFadeImage<I, C> {
- /// A boxed image value. Boxing provides indirection so images can
- /// be cross-fades and cross-fades can be images.
- Image(I),
- /// A color value.
- Color(C),
-}
-
-pub use self::GenericCrossFade as CrossFade;
-pub use self::GenericCrossFadeElement as CrossFadeElement;
-pub use self::GenericCrossFadeImage as CrossFadeImage;
-
-/// https://drafts.csswg.org/css-images-4/#image-set-notation
-#[derive(Clone, Debug, MallocSizeOf, PartialEq, ToCss, ToResolvedValue, ToShmem)]
-#[css(comma, function = "image-set")]
-#[repr(C)]
-pub struct GenericImageSet<Image, Resolution> {
- /// The index of the selected candidate. usize::MAX for specified values or invalid images.
- #[css(skip)]
- pub selected_index: usize,
-
- /// All of the image and resolution pairs.
- #[css(iterable)]
- pub items: crate::OwnedSlice<GenericImageSetItem<Image, Resolution>>,
-}
-
-/// An optional percent and a cross fade image.
-#[derive(Clone, Debug, MallocSizeOf, PartialEq, ToComputedValue, ToResolvedValue, ToShmem)]
-#[repr(C)]
-pub struct GenericImageSetItem<Image, Resolution> {
- /// `<image>`. `<string>` is converted to `Image::Url` at parse time.
- pub image: Image,
- /// The `<resolution>`.
- ///
- /// TODO: Skip serialization if it is 1x.
- pub resolution: Resolution,
-
- /// The `type(<string>)`
- /// (Optional) Specify the image's MIME type
- pub mime_type: crate::OwnedStr,
-
- /// True if mime_type has been specified
- pub has_mime_type: bool,
-}
-
-impl<I: style_traits::ToCss, R: style_traits::ToCss> ToCss for GenericImageSetItem<I, R> {
- fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
- where
- W: fmt::Write,
- {
- self.image.to_css(dest)?;
- dest.write_char(' ')?;
- self.resolution.to_css(dest)?;
-
- if self.has_mime_type {
- dest.write_char(' ')?;
- dest.write_str("type(")?;
- self.mime_type.to_css(dest)?;
- dest.write_char(')')?;
- }
- Ok(())
- }
-}
-
-pub use self::GenericImageSet as ImageSet;
-pub use self::GenericImageSetItem as ImageSetItem;
-
-/// A CSS gradient.
-/// <https://drafts.csswg.org/css-images/#gradients>
-#[derive(Clone, Debug, MallocSizeOf, PartialEq, ToComputedValue, ToResolvedValue, ToShmem)]
-#[repr(C)]
-pub enum GenericGradient<
- LineDirection,
- LengthPercentage,
- NonNegativeLength,
- NonNegativeLengthPercentage,
- Position,
- Angle,
- AngleOrPercentage,
- Color,
-> {
- /// A linear gradient.
- Linear {
- /// Line direction
- direction: LineDirection,
- /// The color stops and interpolation hints.
- items: crate::OwnedSlice<GenericGradientItem<Color, LengthPercentage>>,
- /// True if this is a repeating gradient.
- repeating: bool,
- /// Compatibility mode.
- compat_mode: GradientCompatMode,
- },
- /// A radial gradient.
- Radial {
- /// Shape of gradient
- shape: GenericEndingShape<NonNegativeLength, NonNegativeLengthPercentage>,
- /// Center of gradient
- position: Position,
- /// The color stops and interpolation hints.
- items: crate::OwnedSlice<GenericGradientItem<Color, LengthPercentage>>,
- /// True if this is a repeating gradient.
- repeating: bool,
- /// Compatibility mode.
- compat_mode: GradientCompatMode,
- },
- /// A conic gradient.
- Conic {
- /// Start angle of gradient
- angle: Angle,
- /// Center of gradient
- position: Position,
- /// The color stops and interpolation hints.
- items: crate::OwnedSlice<GenericGradientItem<Color, AngleOrPercentage>>,
- /// True if this is a repeating gradient.
- repeating: bool,
- },
-}
-
-pub use self::GenericGradient as Gradient;
-
-#[derive(
- Clone, Copy, Debug, MallocSizeOf, PartialEq, ToComputedValue, ToResolvedValue, ToShmem,
-)]
-#[repr(u8)]
-/// Whether we used the modern notation or the compatibility `-webkit`, `-moz` prefixes.
-pub enum GradientCompatMode {
- /// Modern syntax.
- Modern,
- /// `-webkit` prefix.
- WebKit,
- /// `-moz` prefix
- Moz,
-}
-
-/// A radial gradient's ending shape.
-#[derive(
- Clone, Copy, Debug, MallocSizeOf, PartialEq, ToComputedValue, ToCss, ToResolvedValue, ToShmem,
-)]
-#[repr(C, u8)]
-pub enum GenericEndingShape<NonNegativeLength, NonNegativeLengthPercentage> {
- /// A circular gradient.
- Circle(GenericCircle<NonNegativeLength>),
- /// An elliptic gradient.
- Ellipse(GenericEllipse<NonNegativeLengthPercentage>),
-}
-
-pub use self::GenericEndingShape as EndingShape;
-
-/// A circle shape.
-#[derive(
- Clone, Copy, Debug, MallocSizeOf, PartialEq, ToComputedValue, ToResolvedValue, ToShmem,
-)]
-#[repr(C, u8)]
-pub enum GenericCircle<NonNegativeLength> {
- /// A circle radius.
- Radius(NonNegativeLength),
- /// A circle extent.
- Extent(ShapeExtent),
-}
-
-pub use self::GenericCircle as Circle;
-
-/// An ellipse shape.
-#[derive(
- Clone, Copy, Debug, MallocSizeOf, PartialEq, ToComputedValue, ToCss, ToResolvedValue, ToShmem,
-)]
-#[repr(C, u8)]
-pub enum GenericEllipse<NonNegativeLengthPercentage> {
- /// An ellipse pair of radii.
- Radii(NonNegativeLengthPercentage, NonNegativeLengthPercentage),
- /// An ellipse extent.
- Extent(ShapeExtent),
-}
-
-pub use self::GenericEllipse as Ellipse;
-
-/// <https://drafts.csswg.org/css-images/#typedef-extent-keyword>
-#[allow(missing_docs)]
-#[cfg_attr(feature = "servo", derive(Deserialize, Serialize))]
-#[derive(
- Clone,
- Copy,
- Debug,
- Eq,
- MallocSizeOf,
- Parse,
- PartialEq,
- ToComputedValue,
- ToCss,
- ToResolvedValue,
- ToShmem,
-)]
-#[repr(u8)]
-pub enum ShapeExtent {
- ClosestSide,
- FarthestSide,
- ClosestCorner,
- FarthestCorner,
- Contain,
- Cover,
-}
-
-/// A gradient item.
-/// <https://drafts.csswg.org/css-images-4/#color-stop-syntax>
-#[derive(
- Clone, Copy, Debug, MallocSizeOf, PartialEq, ToComputedValue, ToCss, ToResolvedValue, ToShmem,
-)]
-#[repr(C, u8)]
-pub enum GenericGradientItem<Color, T> {
- /// A simple color stop, without position.
- SimpleColorStop(Color),
- /// A complex color stop, with a position.
- ComplexColorStop {
- /// The color for the stop.
- color: Color,
- /// The position for the stop.
- position: T,
- },
- /// An interpolation hint.
- InterpolationHint(T),
-}
-
-pub use self::GenericGradientItem as GradientItem;
-
-/// A color stop.
-/// <https://drafts.csswg.org/css-images/#typedef-color-stop-list>
-#[derive(
- Clone, Copy, Debug, MallocSizeOf, PartialEq, ToComputedValue, ToCss, ToResolvedValue, ToShmem,
-)]
-pub struct ColorStop<Color, T> {
- /// The color of this stop.
- pub color: Color,
- /// The position of this stop.
- pub position: Option<T>,
-}
-
-impl<Color, T> ColorStop<Color, T> {
- /// Convert the color stop into an appropriate `GradientItem`.
- #[inline]
- pub fn into_item(self) -> GradientItem<Color, T> {
- match self.position {
- Some(position) => GradientItem::ComplexColorStop {
- color: self.color,
- position,
- },
- None => GradientItem::SimpleColorStop(self.color),
- }
- }
-}
-
-/// Specified values for a paint worklet.
-/// <https://drafts.css-houdini.org/css-paint-api/>
-#[cfg_attr(feature = "servo", derive(MallocSizeOf))]
-#[derive(Clone, Debug, PartialEq, ToComputedValue, ToResolvedValue, ToShmem)]
-pub struct PaintWorklet {
- /// The name the worklet was registered with.
- pub name: Atom,
- /// The arguments for the worklet.
- /// TODO: store a parsed representation of the arguments.
- #[cfg_attr(feature = "servo", ignore_malloc_size_of = "Arc")]
- #[compute(no_field_bound)]
- #[resolve(no_field_bound)]
- pub arguments: Vec<Arc<custom_properties::SpecifiedValue>>,
-}
-
-impl ::style_traits::SpecifiedValueInfo for PaintWorklet {}
-
-impl ToCss for PaintWorklet {
- fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
- where
- W: Write,
- {
- dest.write_str("paint(")?;
- serialize_atom_identifier(&self.name, dest)?;
- for argument in &self.arguments {
- dest.write_str(", ")?;
- argument.to_css(dest)?;
- }
- dest.write_char(')')
- }
-}
-
-/// Values for `moz-image-rect`.
-///
-/// `-moz-image-rect(<uri>, top, right, bottom, left);`
-#[allow(missing_docs)]
-#[derive(
- Clone,
- Debug,
- MallocSizeOf,
- PartialEq,
- SpecifiedValueInfo,
- ToComputedValue,
- ToCss,
- ToResolvedValue,
- ToShmem,
-)]
-#[css(comma, function = "-moz-image-rect")]
-#[repr(C)]
-pub struct GenericMozImageRect<NumberOrPercentage, MozImageRectUrl> {
- pub url: MozImageRectUrl,
- pub top: NumberOrPercentage,
- pub right: NumberOrPercentage,
- pub bottom: NumberOrPercentage,
- pub left: NumberOrPercentage,
-}
-
-pub use self::GenericMozImageRect as MozImageRect;
-
-impl<G, R, U, C, P, Resolution> fmt::Debug for Image<G, R, U, C, P, Resolution>
-where
- Image<G, R, U, C, P, Resolution>: ToCss,
-{
- fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
- self.to_css(&mut CssWriter::new(f))
- }
-}
-
-impl<G, R, U, C, P, Resolution> ToCss for Image<G, R, U, C, P, Resolution>
-where
- G: ToCss,
- R: ToCss,
- U: ToCss,
- C: ToCss,
- P: ToCss,
- Resolution: ToCss,
-{
- fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
- where
- W: Write,
- {
- match *self {
- Image::None => dest.write_str("none"),
- Image::Url(ref url) => url.to_css(dest),
- Image::Gradient(ref gradient) => gradient.to_css(dest),
- Image::Rect(ref rect) => rect.to_css(dest),
- #[cfg(feature = "servo")]
- Image::PaintWorklet(ref paint_worklet) => paint_worklet.to_css(dest),
- #[cfg(feature = "gecko")]
- Image::Element(ref selector) => {
- dest.write_str("-moz-element(#")?;
- serialize_atom_identifier(selector, dest)?;
- dest.write_char(')')
- },
- Image::ImageSet(ref is) => is.to_css(dest),
- Image::CrossFade(ref cf) => cf.to_css(dest),
- }
- }
-}
-
-impl<D, LP, NL, NLP, P, A: Zero, AoP, C> ToCss for Gradient<D, LP, NL, NLP, P, A, AoP, C>
-where
- D: LineDirection,
- LP: ToCss,
- NL: ToCss,
- NLP: ToCss,
- P: PositionComponent + ToCss,
- A: ToCss,
- AoP: ToCss,
- C: ToCss,
-{
- fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
- where
- W: Write,
- {
- let (compat_mode, repeating) = match *self {
- Gradient::Linear {
- compat_mode,
- repeating,
- ..
- } => (compat_mode, repeating),
- Gradient::Radial {
- compat_mode,
- repeating,
- ..
- } => (compat_mode, repeating),
- Gradient::Conic { repeating, .. } => (GradientCompatMode::Modern, repeating),
- };
-
- match compat_mode {
- GradientCompatMode::WebKit => dest.write_str("-webkit-")?,
- GradientCompatMode::Moz => dest.write_str("-moz-")?,
- _ => {},
- }
-
- if repeating {
- dest.write_str("repeating-")?;
- }
-
- match *self {
- Gradient::Linear {
- ref direction,
- ref items,
- compat_mode,
- ..
- } => {
- dest.write_str("linear-gradient(")?;
- let mut skip_comma = if !direction.points_downwards(compat_mode) {
- direction.to_css(dest, compat_mode)?;
- false
- } else {
- true
- };
- for item in &**items {
- if !skip_comma {
- dest.write_str(", ")?;
- }
- skip_comma = false;
- item.to_css(dest)?;
- }
- },
- Gradient::Radial {
- ref shape,
- ref position,
- ref items,
- compat_mode,
- ..
- } => {
- dest.write_str("radial-gradient(")?;
- let omit_shape = match *shape {
- EndingShape::Ellipse(Ellipse::Extent(ShapeExtent::Cover)) |
- EndingShape::Ellipse(Ellipse::Extent(ShapeExtent::FarthestCorner)) => true,
- _ => false,
- };
- let omit_position = position.is_center();
- if compat_mode == GradientCompatMode::Modern {
- if !omit_shape {
- shape.to_css(dest)?;
- if !omit_position {
- dest.write_char(' ')?;
- }
- }
- if !omit_position {
- dest.write_str("at ")?;
- position.to_css(dest)?;
- }
- } else {
- if !omit_position {
- position.to_css(dest)?;
- if !omit_shape {
- dest.write_str(", ")?;
- }
- }
- if !omit_shape {
- shape.to_css(dest)?;
- }
- }
- let mut skip_comma = omit_shape && omit_position;
- for item in &**items {
- if !skip_comma {
- dest.write_str(", ")?;
- }
- skip_comma = false;
- item.to_css(dest)?;
- }
- },
- Gradient::Conic {
- ref angle,
- ref position,
- ref items,
- ..
- } => {
- dest.write_str("conic-gradient(")?;
- let omit_angle = angle.is_zero();
- let omit_position = position.is_center();
- if !omit_angle {
- dest.write_str("from ")?;
- angle.to_css(dest)?;
- if !omit_position {
- dest.write_char(' ')?;
- }
- }
- if !omit_position {
- dest.write_str("at ")?;
- position.to_css(dest)?;
- }
- let mut skip_comma = omit_angle && omit_position;
- for item in &**items {
- if !skip_comma {
- dest.write_str(", ")?;
- }
- skip_comma = false;
- item.to_css(dest)?;
- }
- },
- }
- dest.write_char(')')
- }
-}
-
-/// The direction of a linear gradient.
-pub trait LineDirection {
- /// Whether this direction points towards, and thus can be omitted.
- fn points_downwards(&self, compat_mode: GradientCompatMode) -> bool;
-
- /// Serialises this direction according to the compatibility mode.
- fn to_css<W>(&self, dest: &mut CssWriter<W>, compat_mode: GradientCompatMode) -> fmt::Result
- where
- W: Write;
-}
-
-impl<L> ToCss for Circle<L>
-where
- L: ToCss,
-{
- fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
- where
- W: Write,
- {
- match *self {
- Circle::Extent(ShapeExtent::FarthestCorner) | Circle::Extent(ShapeExtent::Cover) => {
- dest.write_str("circle")
- },
- Circle::Extent(keyword) => {
- dest.write_str("circle ")?;
- keyword.to_css(dest)
- },
- Circle::Radius(ref length) => length.to_css(dest),
- }
- }
-}
diff --git a/components/style/values/generics/length.rs b/components/style/values/generics/length.rs
deleted file mode 100644
index 2eb7050870f..00000000000
--- a/components/style/values/generics/length.rs
+++ /dev/null
@@ -1,314 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
-
-//! Generic types for CSS values related to length.
-
-use crate::parser::{Parse, ParserContext};
-use crate::Zero;
-use cssparser::Parser;
-use style_traits::ParseError;
-
-/// A `<length-percentage> | auto` value.
-#[allow(missing_docs)]
-#[derive(
- Animate,
- Clone,
- ComputeSquaredDistance,
- Copy,
- Debug,
- MallocSizeOf,
- PartialEq,
- SpecifiedValueInfo,
- ToAnimatedValue,
- ToAnimatedZero,
- ToComputedValue,
- ToCss,
- ToResolvedValue,
- ToShmem,
-)]
-#[cfg_attr(feature = "servo", derive(Deserialize, Serialize))]
-#[repr(C, u8)]
-pub enum GenericLengthPercentageOrAuto<LengthPercent> {
- LengthPercentage(LengthPercent),
- Auto,
-}
-
-pub use self::GenericLengthPercentageOrAuto as LengthPercentageOrAuto;
-
-impl<LengthPercentage> LengthPercentageOrAuto<LengthPercentage> {
- /// `auto` value.
- #[inline]
- pub fn auto() -> Self {
- LengthPercentageOrAuto::Auto
- }
-
- /// Whether this is the `auto` value.
- #[inline]
- pub fn is_auto(&self) -> bool {
- matches!(*self, LengthPercentageOrAuto::Auto)
- }
-
- /// A helper function to parse this with quirks or not and so forth.
- pub fn parse_with<'i, 't>(
- context: &ParserContext,
- input: &mut Parser<'i, 't>,
- parser: impl FnOnce(
- &ParserContext,
- &mut Parser<'i, 't>,
- ) -> Result<LengthPercentage, ParseError<'i>>,
- ) -> Result<Self, ParseError<'i>> {
- if input.try_parse(|i| i.expect_ident_matching("auto")).is_ok() {
- return Ok(LengthPercentageOrAuto::Auto);
- }
-
- Ok(LengthPercentageOrAuto::LengthPercentage(parser(
- context, input,
- )?))
- }
-}
-
-impl<T> LengthPercentageOrAuto<T>
-where
- T: Clone,
-{
- /// Resolves `auto` values by calling `f`.
- #[inline]
- pub fn auto_is(&self, f: impl FnOnce() -> T) -> T {
- match self {
- LengthPercentageOrAuto::LengthPercentage(length) => length.clone(),
- LengthPercentageOrAuto::Auto => f(),
- }
- }
-
- /// Returns the non-`auto` value, if any.
- #[inline]
- pub fn non_auto(&self) -> Option<T> {
- match self {
- LengthPercentageOrAuto::LengthPercentage(length) => Some(length.clone()),
- LengthPercentageOrAuto::Auto => None,
- }
- }
-
- /// Maps the length of this value.
- pub fn map<U>(&self, f: impl FnOnce(T) -> U) -> LengthPercentageOrAuto<U> {
- match self {
- LengthPercentageOrAuto::LengthPercentage(l) => {
- LengthPercentageOrAuto::LengthPercentage(f(l.clone()))
- },
- LengthPercentageOrAuto::Auto => LengthPercentageOrAuto::Auto,
- }
- }
-}
-
-impl<LengthPercentage: Zero> Zero for LengthPercentageOrAuto<LengthPercentage> {
- fn zero() -> Self {
- LengthPercentageOrAuto::LengthPercentage(Zero::zero())
- }
-
- fn is_zero(&self) -> bool {
- match *self {
- LengthPercentageOrAuto::LengthPercentage(ref l) => l.is_zero(),
- LengthPercentageOrAuto::Auto => false,
- }
- }
-}
-
-impl<LengthPercentage: Parse> Parse for LengthPercentageOrAuto<LengthPercentage> {
- fn parse<'i, 't>(
- context: &ParserContext,
- input: &mut Parser<'i, 't>,
- ) -> Result<Self, ParseError<'i>> {
- Self::parse_with(context, input, LengthPercentage::parse)
- }
-}
-
-/// A generic value for the `width`, `height`, `min-width`, or `min-height` property.
-///
-/// Unlike `max-width` or `max-height` properties, a Size can be `auto`,
-/// and cannot be `none`.
-///
-/// Note that it only accepts non-negative values.
-#[allow(missing_docs)]
-#[derive(
- Animate,
- Clone,
- ComputeSquaredDistance,
- Copy,
- Debug,
- MallocSizeOf,
- PartialEq,
- SpecifiedValueInfo,
- ToAnimatedValue,
- ToAnimatedZero,
- ToComputedValue,
- ToCss,
- ToResolvedValue,
- ToShmem,
-)]
-#[repr(C, u8)]
-pub enum GenericSize<LengthPercent> {
- LengthPercentage(LengthPercent),
- Auto,
- #[cfg(feature = "gecko")]
- #[animation(error)]
- MaxContent,
- #[cfg(feature = "gecko")]
- #[animation(error)]
- MinContent,
- #[cfg(feature = "gecko")]
- #[animation(error)]
- FitContent,
- #[cfg(feature = "gecko")]
- #[animation(error)]
- MozAvailable,
- #[cfg(feature = "gecko")]
- #[animation(error)]
- #[css(function = "fit-content")]
- FitContentFunction(LengthPercent)
-}
-
-pub use self::GenericSize as Size;
-
-impl<LengthPercentage> Size<LengthPercentage> {
- /// `auto` value.
- #[inline]
- pub fn auto() -> Self {
- Size::Auto
- }
-
- /// Returns whether we're the auto value.
- #[inline]
- pub fn is_auto(&self) -> bool {
- matches!(*self, Size::Auto)
- }
-}
-
-/// A generic value for the `max-width` or `max-height` property.
-#[allow(missing_docs)]
-#[derive(
- Animate,
- Clone,
- ComputeSquaredDistance,
- Copy,
- Debug,
- MallocSizeOf,
- PartialEq,
- SpecifiedValueInfo,
- ToAnimatedValue,
- ToAnimatedZero,
- ToComputedValue,
- ToCss,
- ToResolvedValue,
- ToShmem,
-)]
-#[repr(C, u8)]
-pub enum GenericMaxSize<LengthPercent> {
- LengthPercentage(LengthPercent),
- None,
- #[cfg(feature = "gecko")]
- #[animation(error)]
- MaxContent,
- #[cfg(feature = "gecko")]
- #[animation(error)]
- MinContent,
- #[cfg(feature = "gecko")]
- #[animation(error)]
- FitContent,
- #[cfg(feature = "gecko")]
- #[animation(error)]
- MozAvailable,
- #[cfg(feature = "gecko")]
- #[animation(error)]
- #[css(function = "fit-content")]
- FitContentFunction(LengthPercent),
-}
-
-pub use self::GenericMaxSize as MaxSize;
-
-impl<LengthPercentage> MaxSize<LengthPercentage> {
- /// `none` value.
- #[inline]
- pub fn none() -> Self {
- MaxSize::None
- }
-}
-
-/// A generic `<length>` | `<number>` value for the `tab-size` property.
-#[derive(
- Animate,
- Clone,
- ComputeSquaredDistance,
- Copy,
- Debug,
- MallocSizeOf,
- Parse,
- PartialEq,
- SpecifiedValueInfo,
- ToAnimatedValue,
- ToAnimatedZero,
- ToComputedValue,
- ToCss,
- ToResolvedValue,
- ToShmem,
-)]
-#[repr(C, u8)]
-pub enum GenericLengthOrNumber<L, N> {
- /// A number.
- ///
- /// NOTE: Numbers need to be before lengths, in order to parse them
- /// first, since `0` should be a number, not the `0px` length.
- Number(N),
- /// A length.
- Length(L),
-}
-
-pub use self::GenericLengthOrNumber as LengthOrNumber;
-
-impl<L, N: Zero> Zero for LengthOrNumber<L, N> {
- fn zero() -> Self {
- LengthOrNumber::Number(Zero::zero())
- }
-
- fn is_zero(&self) -> bool {
- match *self {
- LengthOrNumber::Number(ref n) => n.is_zero(),
- LengthOrNumber::Length(..) => false,
- }
- }
-}
-
-/// A generic `<length-percentage>` | normal` value.
-#[derive(
- Animate,
- Clone,
- ComputeSquaredDistance,
- Copy,
- Debug,
- MallocSizeOf,
- Parse,
- PartialEq,
- SpecifiedValueInfo,
- ToAnimatedValue,
- ToAnimatedZero,
- ToComputedValue,
- ToCss,
- ToResolvedValue,
- ToShmem,
-)]
-#[repr(C, u8)]
-#[allow(missing_docs)]
-pub enum GenericLengthPercentageOrNormal<LengthPercent> {
- LengthPercentage(LengthPercent),
- Normal,
-}
-
-pub use self::GenericLengthPercentageOrNormal as LengthPercentageOrNormal;
-
-impl<LengthPercent> LengthPercentageOrNormal<LengthPercent> {
- /// Returns the normal value.
- #[inline]
- pub fn normal() -> Self {
- LengthPercentageOrNormal::Normal
- }
-}
diff --git a/components/style/values/generics/mod.rs b/components/style/values/generics/mod.rs
deleted file mode 100644
index 237d3c16a32..00000000000
--- a/components/style/values/generics/mod.rs
+++ /dev/null
@@ -1,386 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
-
-//! Generic types that share their serialization implementations
-//! for both specified and computed values.
-
-use super::CustomIdent;
-use crate::counter_style::{parse_counter_style_name, Symbols};
-use crate::parser::{Parse, ParserContext};
-use crate::Zero;
-use cssparser::Parser;
-use std::ops::Add;
-use style_traits::{KeywordsCollectFn, ParseError, SpecifiedValueInfo, StyleParseErrorKind};
-
-pub mod animation;
-pub mod background;
-pub mod basic_shape;
-pub mod border;
-#[path = "box.rs"]
-pub mod box_;
-pub mod calc;
-pub mod color;
-pub mod column;
-pub mod counters;
-pub mod easing;
-pub mod effects;
-pub mod flex;
-pub mod font;
-pub mod grid;
-pub mod image;
-pub mod length;
-pub mod motion;
-pub mod page;
-pub mod position;
-pub mod ratio;
-pub mod rect;
-pub mod size;
-pub mod svg;
-pub mod text;
-pub mod transform;
-pub mod ui;
-pub mod url;
-
-/// https://drafts.csswg.org/css-counter-styles/#typedef-symbols-type
-#[allow(missing_docs)]
-#[cfg_attr(feature = "servo", derive(Deserialize, Serialize))]
-#[derive(
- Clone,
- Copy,
- Debug,
- Eq,
- MallocSizeOf,
- Parse,
- PartialEq,
- ToComputedValue,
- ToCss,
- ToResolvedValue,
- ToShmem,
-)]
-#[repr(u8)]
-pub enum SymbolsType {
- Cyclic,
- Numeric,
- Alphabetic,
- Symbolic,
- Fixed,
-}
-
-/// <https://drafts.csswg.org/css-counter-styles/#typedef-counter-style>
-///
-/// Note that 'none' is not a valid name.
-#[cfg_attr(feature = "gecko", derive(MallocSizeOf))]
-#[derive(Clone, Debug, Eq, PartialEq, ToComputedValue, ToCss, ToResolvedValue, ToShmem)]
-#[repr(u8)]
-pub enum CounterStyle {
- /// `<counter-style-name>`
- Name(CustomIdent),
- /// `symbols()`
- #[css(function)]
- Symbols(#[css(skip_if = "is_symbolic")] SymbolsType, Symbols),
-}
-
-#[inline]
-fn is_symbolic(symbols_type: &SymbolsType) -> bool {
- *symbols_type == SymbolsType::Symbolic
-}
-
-impl CounterStyle {
- /// disc value
- pub fn disc() -> Self {
- CounterStyle::Name(CustomIdent(atom!("disc")))
- }
-
- /// decimal value
- pub fn decimal() -> Self {
- CounterStyle::Name(CustomIdent(atom!("decimal")))
- }
-
- /// Is this a bullet? (i.e. `list-style-type: disc|circle|square|disclosure-closed|disclosure-open`)
- #[inline]
- pub fn is_bullet(&self) -> bool {
- match self {
- CounterStyle::Name(CustomIdent(ref name)) => {
- name == &atom!("disc") ||
- name == &atom!("circle") ||
- name == &atom!("square") ||
- name == &atom!("disclosure-closed") ||
- name == &atom!("disclosure-open")
- },
- _ => false,
- }
- }
-}
-
-impl Parse for CounterStyle {
- fn parse<'i, 't>(
- context: &ParserContext,
- input: &mut Parser<'i, 't>,
- ) -> Result<Self, ParseError<'i>> {
- if let Ok(name) = input.try_parse(|i| parse_counter_style_name(i)) {
- return Ok(CounterStyle::Name(name));
- }
- input.expect_function_matching("symbols")?;
- input.parse_nested_block(|input| {
- let symbols_type = input
- .try_parse(SymbolsType::parse)
- .unwrap_or(SymbolsType::Symbolic);
- let symbols = Symbols::parse(context, input)?;
- // There must be at least two symbols for alphabetic or
- // numeric system.
- if (symbols_type == SymbolsType::Alphabetic || symbols_type == SymbolsType::Numeric) &&
- symbols.0.len() < 2
- {
- return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError));
- }
- // Identifier is not allowed in symbols() function.
- if symbols.0.iter().any(|sym| !sym.is_allowed_in_symbols()) {
- return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError));
- }
- Ok(CounterStyle::Symbols(symbols_type, symbols))
- })
- }
-}
-
-impl SpecifiedValueInfo for CounterStyle {
- fn collect_completion_keywords(f: KeywordsCollectFn) {
- // XXX The best approach for implementing this is probably
- // having a CounterStyleName type wrapping CustomIdent, and
- // put the predefined list for that type in counter_style mod.
- // But that's a non-trivial change itself, so we use a simpler
- // approach here.
- macro_rules! predefined {
- ($($name:expr,)+) => {
- f(&["symbols", $($name,)+])
- }
- }
- include!("../../counter_style/predefined.rs");
- }
-}
-
-/// A wrapper of Non-negative values.
-#[cfg_attr(feature = "servo", derive(Deserialize, Serialize))]
-#[derive(
- Animate,
- Clone,
- ComputeSquaredDistance,
- Copy,
- Debug,
- Hash,
- MallocSizeOf,
- PartialEq,
- PartialOrd,
- SpecifiedValueInfo,
- ToAnimatedZero,
- ToComputedValue,
- ToCss,
- ToResolvedValue,
- ToShmem,
-)]
-#[repr(transparent)]
-pub struct NonNegative<T>(pub T);
-
-impl<T: Add<Output = T>> Add<NonNegative<T>> for NonNegative<T> {
- type Output = Self;
-
- fn add(self, other: Self) -> Self {
- NonNegative(self.0 + other.0)
- }
-}
-
-impl<T: Zero> Zero for NonNegative<T> {
- fn is_zero(&self) -> bool {
- self.0.is_zero()
- }
-
- fn zero() -> Self {
- NonNegative(T::zero())
- }
-}
-
-/// A wrapper of greater-than-or-equal-to-one values.
-#[cfg_attr(feature = "servo", derive(Deserialize, Serialize))]
-#[derive(
- Animate,
- Clone,
- ComputeSquaredDistance,
- Copy,
- Debug,
- MallocSizeOf,
- PartialEq,
- PartialOrd,
- SpecifiedValueInfo,
- ToAnimatedZero,
- ToComputedValue,
- ToCss,
- ToResolvedValue,
- ToShmem,
-)]
-pub struct GreaterThanOrEqualToOne<T>(pub T);
-
-/// A wrapper of values between zero and one.
-#[cfg_attr(feature = "servo", derive(Deserialize, Serialize))]
-#[derive(
- Animate,
- Clone,
- ComputeSquaredDistance,
- Copy,
- Debug,
- Hash,
- MallocSizeOf,
- PartialEq,
- PartialOrd,
- SpecifiedValueInfo,
- ToAnimatedZero,
- ToComputedValue,
- ToCss,
- ToResolvedValue,
- ToShmem,
-)]
-#[repr(transparent)]
-pub struct ZeroToOne<T>(pub T);
-
-/// A clip rect for clip and image-region
-#[allow(missing_docs)]
-#[derive(
- Clone,
- ComputeSquaredDistance,
- Copy,
- Debug,
- MallocSizeOf,
- PartialEq,
- SpecifiedValueInfo,
- ToAnimatedValue,
- ToAnimatedZero,
- ToComputedValue,
- ToCss,
- ToResolvedValue,
- ToShmem,
-)]
-#[css(function = "rect", comma)]
-#[repr(C)]
-pub struct GenericClipRect<LengthOrAuto> {
- pub top: LengthOrAuto,
- pub right: LengthOrAuto,
- pub bottom: LengthOrAuto,
- pub left: LengthOrAuto,
-}
-
-pub use self::GenericClipRect as ClipRect;
-
-/// Either a clip-rect or `auto`.
-#[allow(missing_docs)]
-#[derive(
- Animate,
- Clone,
- ComputeSquaredDistance,
- Copy,
- Debug,
- MallocSizeOf,
- Parse,
- PartialEq,
- SpecifiedValueInfo,
- ToAnimatedValue,
- ToAnimatedZero,
- ToComputedValue,
- ToCss,
- ToResolvedValue,
- ToShmem,
-)]
-#[repr(C, u8)]
-pub enum GenericClipRectOrAuto<R> {
- Auto,
- Rect(R),
-}
-
-pub use self::GenericClipRectOrAuto as ClipRectOrAuto;
-
-impl<L> ClipRectOrAuto<L> {
- /// Returns the `auto` value.
- #[inline]
- pub fn auto() -> Self {
- ClipRectOrAuto::Auto
- }
-
- /// Returns whether this value is the `auto` value.
- #[inline]
- pub fn is_auto(&self) -> bool {
- matches!(*self, ClipRectOrAuto::Auto)
- }
-}
-
-pub use page::PageSize;
-
-/// An optional value, much like `Option<T>`, but with a defined struct layout
-/// to be able to use it from C++ as well.
-///
-/// Note that this is relatively inefficient, struct-layout-wise, as you have
-/// one byte for the tag, but padding to the alignment of T. If you have
-/// multiple optional values and care about struct compactness, you might be
-/// better off "coalescing" the combinations into a parent enum. But that
-/// shouldn't matter for most use cases.
-#[allow(missing_docs)]
-#[derive(
- Animate,
- Clone,
- ComputeSquaredDistance,
- Copy,
- Debug,
- MallocSizeOf,
- Parse,
- PartialEq,
- SpecifiedValueInfo,
- ToAnimatedValue,
- ToAnimatedZero,
- ToComputedValue,
- ToCss,
- ToResolvedValue,
- ToShmem,
- Serialize,
- Deserialize,
-)]
-#[repr(C, u8)]
-pub enum Optional<T> {
- #[css(skip)]
- None,
- Some(T),
-}
-
-impl<T> Optional<T> {
- /// Returns whether this value is present.
- pub fn is_some(&self) -> bool {
- matches!(*self, Self::Some(..))
- }
-
- /// Returns whether this value is not present.
- pub fn is_none(&self) -> bool {
- matches!(*self, Self::None)
- }
-
- /// Turns this Optional<> into a regular rust Option<>.
- pub fn into_rust(self) -> Option<T> {
- match self {
- Self::Some(v) => Some(v),
- Self::None => None,
- }
- }
-
- /// Return a reference to the containing value, if any, as a plain rust
- /// Option<>.
- pub fn as_ref(&self) -> Option<&T> {
- match *self {
- Self::Some(ref v) => Some(v),
- Self::None => None,
- }
- }
-}
-
-impl<T> From<Option<T>> for Optional<T> {
- fn from(rust: Option<T>) -> Self {
- match rust {
- Some(t) => Self::Some(t),
- None => Self::None,
- }
- }
-}
diff --git a/components/style/values/generics/motion.rs b/components/style/values/generics/motion.rs
deleted file mode 100644
index 92c1d004bb8..00000000000
--- a/components/style/values/generics/motion.rs
+++ /dev/null
@@ -1,205 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
-
-//! Generic types for CSS Motion Path.
-
-use crate::values::animated::ToAnimatedZero;
-use crate::values::generics::position::{GenericPosition, GenericPositionOrAuto};
-use crate::values::specified::SVGPathData;
-use std::fmt::{self, Write};
-use style_traits::{CssWriter, ToCss};
-
-/// The <size> in ray() function.
-///
-/// https://drafts.fxtf.org/motion-1/#valdef-offsetpath-size
-#[allow(missing_docs)]
-#[derive(
- Animate,
- Clone,
- ComputeSquaredDistance,
- Copy,
- Debug,
- Deserialize,
- MallocSizeOf,
- Parse,
- PartialEq,
- Serialize,
- SpecifiedValueInfo,
- ToComputedValue,
- ToCss,
- ToResolvedValue,
- ToShmem,
-)]
-#[repr(u8)]
-pub enum RaySize {
- ClosestSide,
- ClosestCorner,
- FarthestSide,
- FarthestCorner,
- Sides,
-}
-
-/// The `ray()` function, `ray( [ <angle> && <size> && contain? && [at <position>]? ] )`
-///
-/// https://drafts.fxtf.org/motion-1/#valdef-offsetpath-ray
-#[derive(
- Animate,
- Clone,
- ComputeSquaredDistance,
- Debug,
- Deserialize,
- MallocSizeOf,
- PartialEq,
- Serialize,
- SpecifiedValueInfo,
- ToComputedValue,
- ToResolvedValue,
- ToShmem,
-)]
-#[repr(C)]
-pub struct GenericRayFunction<Angle, Position> {
- /// The bearing angle with `0deg` pointing up and positive angles
- /// representing clockwise rotation.
- pub angle: Angle,
- /// Decide the path length used when `offset-distance` is expressed
- /// as a percentage.
- pub size: RaySize,
- /// Clamp `offset-distance` so that the box is entirely contained
- /// within the path.
- #[animation(constant)]
- pub contain: bool,
- /// The "at <position>" part. If omitted, we use auto to represent it.
- pub position: GenericPositionOrAuto<Position>,
-}
-
-pub use self::GenericRayFunction as RayFunction;
-
-impl<Angle, Position> ToCss for RayFunction<Angle, Position>
-where
- Angle: ToCss,
- Position: ToCss,
-{
- fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
- where
- W: Write,
- {
- self.angle.to_css(dest)?;
-
- if !matches!(self.size, RaySize::ClosestSide) {
- dest.write_char(' ')?;
- self.size.to_css(dest)?;
- }
-
- if self.contain {
- dest.write_str(" contain")?;
- }
-
- if !matches!(self.position, GenericPositionOrAuto::Auto) {
- dest.write_str(" at ")?;
- self.position.to_css(dest)?;
- }
-
- Ok(())
- }
-}
-
-/// The offset-path value.
-///
-/// https://drafts.fxtf.org/motion-1/#offset-path-property
-#[derive(
- Animate,
- Clone,
- ComputeSquaredDistance,
- Debug,
- Deserialize,
- MallocSizeOf,
- PartialEq,
- Serialize,
- SpecifiedValueInfo,
- ToComputedValue,
- ToCss,
- ToResolvedValue,
- ToShmem,
-)]
-#[repr(C, u8)]
-pub enum GenericOffsetPath<RayFunction> {
- // We could merge SVGPathData into ShapeSource, so we could reuse them. However,
- // we don't want to support other value for offset-path, so use SVGPathData only for now.
- /// Path value for path(<string>).
- #[css(function)]
- Path(SVGPathData),
- /// ray() function, which defines a path in the polar coordinate system.
- /// Use Box<> to make sure the size of offset-path is not too large.
- #[css(function)]
- Ray(Box<RayFunction>),
- /// None value.
- #[animation(error)]
- None,
- // Bug 1186329: Implement <basic-shape>, <geometry-box>, and <url>.
-}
-
-pub use self::GenericOffsetPath as OffsetPath;
-
-impl<Ray> OffsetPath<Ray> {
- /// Return None.
- #[inline]
- pub fn none() -> Self {
- OffsetPath::None
- }
-}
-
-impl<Ray> ToAnimatedZero for OffsetPath<Ray> {
- #[inline]
- fn to_animated_zero(&self) -> Result<Self, ()> {
- Err(())
- }
-}
-
-/// The offset-position property, which specifies the offset starting position that is used by the
-/// <offset-path> functions if they don’t specify their own starting position.
-///
-/// https://drafts.fxtf.org/motion-1/#offset-position-property
-#[derive(
- Animate,
- Clone,
- ComputeSquaredDistance,
- Copy,
- Debug,
- Deserialize,
- MallocSizeOf,
- Parse,
- PartialEq,
- Serialize,
- SpecifiedValueInfo,
- ToAnimatedValue,
- ToAnimatedZero,
- ToComputedValue,
- ToCss,
- ToResolvedValue,
- ToShmem,
-)]
-#[repr(C, u8)]
-pub enum GenericOffsetPosition<H, V> {
- /// The element does not have an offset starting position.
- Normal,
- /// The offset starting position is the top-left corner of the box.
- Auto,
- /// The offset starting position is the result of using the <position> to position a 0x0 object
- /// area within the box’s containing block.
- Position(
- #[css(field_bound)]
- #[parse(field_bound)]
- GenericPosition<H, V>,
- ),
-}
-
-pub use self::GenericOffsetPosition as OffsetPosition;
-
-impl<H, V> OffsetPosition<H, V> {
- /// Returns the initial value, auto.
- #[inline]
- pub fn auto() -> Self {
- Self::Auto
- }
-}
diff --git a/components/style/values/generics/page.rs b/components/style/values/generics/page.rs
deleted file mode 100644
index 91f02bc4b37..00000000000
--- a/components/style/values/generics/page.rs
+++ /dev/null
@@ -1,162 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
-
-//! @page at-rule properties
-
-use crate::values::generics::NonNegative;
-use crate::values::specified::length::AbsoluteLength;
-
-/// Page size names.
-///
-/// https://drafts.csswg.org/css-page-3/#typedef-page-size-page-size
-#[derive(
- Clone, Copy, Debug, Eq, MallocSizeOf, Parse, PartialEq, SpecifiedValueInfo, ToCss, ToShmem,
-)]
-#[repr(u8)]
-pub enum PaperSize {
- /// ISO A5 media
- A5,
- /// ISO A4 media
- A4,
- /// ISO A3 media
- A3,
- /// ISO B5 media
- B5,
- /// ISO B4 media
- B4,
- /// JIS B5 media
- JisB5,
- /// JIS B4 media
- JisB4,
- /// North American Letter size
- Letter,
- /// North American Legal size
- Legal,
- /// North American Ledger size
- Ledger,
-}
-
-impl PaperSize {
- /// Gets the long edge length of the paper size
- pub fn long_edge(&self) -> NonNegative<AbsoluteLength> {
- NonNegative(match *self {
- PaperSize::A5 => AbsoluteLength::Mm(210.0),
- PaperSize::A4 => AbsoluteLength::Mm(297.0),
- PaperSize::A3 => AbsoluteLength::Mm(420.0),
- PaperSize::B5 => AbsoluteLength::Mm(250.0),
- PaperSize::B4 => AbsoluteLength::Mm(353.0),
- PaperSize::JisB5 => AbsoluteLength::Mm(257.0),
- PaperSize::JisB4 => AbsoluteLength::Mm(364.0),
- PaperSize::Letter => AbsoluteLength::In(11.0),
- PaperSize::Legal => AbsoluteLength::In(14.0),
- PaperSize::Ledger => AbsoluteLength::In(17.0),
- })
- }
- /// Gets the short edge length of the paper size
- pub fn short_edge(&self) -> NonNegative<AbsoluteLength> {
- NonNegative(match *self {
- PaperSize::A5 => AbsoluteLength::Mm(148.0),
- PaperSize::A4 => AbsoluteLength::Mm(210.0),
- PaperSize::A3 => AbsoluteLength::Mm(297.0),
- PaperSize::B5 => AbsoluteLength::Mm(176.0),
- PaperSize::B4 => AbsoluteLength::Mm(250.0),
- PaperSize::JisB5 => AbsoluteLength::Mm(182.0),
- PaperSize::JisB4 => AbsoluteLength::Mm(257.0),
- PaperSize::Letter => AbsoluteLength::In(8.5),
- PaperSize::Legal => AbsoluteLength::In(8.5),
- PaperSize::Ledger => AbsoluteLength::In(11.0),
- })
- }
-}
-
-/// Page orientation names.
-///
-/// https://drafts.csswg.org/css-page-3/#page-orientation-prop
-#[derive(
- Clone,
- Copy,
- Debug,
- Eq,
- MallocSizeOf,
- Parse,
- PartialEq,
- SpecifiedValueInfo,
- ToComputedValue,
- ToCss,
- ToResolvedValue,
- ToShmem,
-)]
-#[repr(u8)]
-pub enum PageOrientation {
- /// upright
- Upright,
- /// rotate-left (counter-clockwise)
- RotateLeft,
- /// rotate-right (clockwise)
- RotateRight,
-}
-
-/// Paper orientation
-///
-/// https://drafts.csswg.org/css-page-3/#page-size-prop
-#[derive(
- Clone,
- Copy,
- Debug,
- Eq,
- MallocSizeOf,
- Parse,
- PartialEq,
- SpecifiedValueInfo,
- ToCss,
- ToResolvedValue,
- ToShmem,
-)]
-#[repr(u8)]
-pub enum PageSizeOrientation {
- /// Portrait orientation
- Portrait,
- /// Landscape orientation
- Landscape,
-}
-
-#[inline]
-fn is_portrait(orientation: &PageSizeOrientation) -> bool {
- *orientation == PageSizeOrientation::Portrait
-}
-
-/// Page size property
-///
-/// https://drafts.csswg.org/css-page-3/#page-size-prop
-#[derive(Clone, Copy, Debug, MallocSizeOf, PartialEq, SpecifiedValueInfo, ToCss, ToShmem)]
-#[repr(C, u8)]
-pub enum GenericPageSize<S> {
- /// `auto` value.
- Auto,
- /// Page dimensions.
- Size(S),
- /// An orientation with no size.
- Orientation(PageSizeOrientation),
- /// Paper size by name
- PaperSize(
- PaperSize,
- #[css(skip_if = "is_portrait")] PageSizeOrientation,
- ),
-}
-
-pub use self::GenericPageSize as PageSize;
-
-impl<S> PageSize<S> {
- /// `auto` value.
- #[inline]
- pub fn auto() -> Self {
- PageSize::Auto
- }
-
- /// Whether this is the `auto` value.
- #[inline]
- pub fn is_auto(&self) -> bool {
- matches!(*self, PageSize::Auto)
- }
-}
diff --git a/components/style/values/generics/position.rs b/components/style/values/generics/position.rs
deleted file mode 100644
index a6282f463bb..00000000000
--- a/components/style/values/generics/position.rs
+++ /dev/null
@@ -1,237 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
-
-//! Generic types for CSS handling of specified and computed values of
-//! [`position`](https://drafts.csswg.org/css-backgrounds-3/#position)
-
-use crate::values::animated::ToAnimatedZero;
-use crate::values::generics::ratio::Ratio;
-
-/// A generic type for representing a CSS [position](https://drafts.csswg.org/css-values/#position).
-#[derive(
- Animate,
- Clone,
- ComputeSquaredDistance,
- Copy,
- Debug,
- Deserialize,
- MallocSizeOf,
- PartialEq,
- Serialize,
- SpecifiedValueInfo,
- ToAnimatedValue,
- ToAnimatedZero,
- ToComputedValue,
- ToResolvedValue,
- ToShmem,
-)]
-#[repr(C)]
-pub struct GenericPosition<H, V> {
- /// The horizontal component of position.
- pub horizontal: H,
- /// The vertical component of position.
- pub vertical: V,
-}
-
-impl<H, V> PositionComponent for Position<H, V>
-where
- H: PositionComponent,
- V: PositionComponent,
-{
- #[inline]
- fn is_center(&self) -> bool {
- self.horizontal.is_center() && self.vertical.is_center()
- }
-}
-
-pub use self::GenericPosition as Position;
-
-impl<H, V> Position<H, V> {
- /// Returns a new position.
- pub fn new(horizontal: H, vertical: V) -> Self {
- Self {
- horizontal,
- vertical,
- }
- }
-}
-
-/// Implements a method that checks if the position is centered.
-pub trait PositionComponent {
- /// Returns if the position component is 50% or center.
- /// For pixel lengths, it always returns false.
- fn is_center(&self) -> bool;
-}
-
-/// A generic type for representing an `Auto | <position>`.
-/// This is used by <offset-anchor> for now.
-/// https://drafts.fxtf.org/motion-1/#offset-anchor-property
-#[derive(
- Animate,
- Clone,
- ComputeSquaredDistance,
- Copy,
- Debug,
- Deserialize,
- MallocSizeOf,
- Parse,
- PartialEq,
- Serialize,
- SpecifiedValueInfo,
- ToAnimatedZero,
- ToComputedValue,
- ToCss,
- ToResolvedValue,
- ToShmem,
-)]
-#[repr(C, u8)]
-pub enum GenericPositionOrAuto<Pos> {
- /// The <position> value.
- Position(Pos),
- /// The keyword `auto`.
- Auto,
-}
-
-pub use self::GenericPositionOrAuto as PositionOrAuto;
-
-impl<Pos> PositionOrAuto<Pos> {
- /// Return `auto`.
- #[inline]
- pub fn auto() -> Self {
- PositionOrAuto::Auto
- }
-
- /// Return true if it is 'auto'.
- #[inline]
- pub fn is_auto(&self) -> bool {
- matches!(self, PositionOrAuto::Auto)
- }
-}
-
-/// A generic value for the `z-index` property.
-#[derive(
- Animate,
- Clone,
- ComputeSquaredDistance,
- Copy,
- Debug,
- MallocSizeOf,
- PartialEq,
- Parse,
- SpecifiedValueInfo,
- ToAnimatedZero,
- ToComputedValue,
- ToCss,
- ToResolvedValue,
- ToShmem,
-)]
-#[repr(C, u8)]
-pub enum GenericZIndex<I> {
- /// An integer value.
- Integer(I),
- /// The keyword `auto`.
- Auto,
-}
-
-pub use self::GenericZIndex as ZIndex;
-
-impl<Integer> ZIndex<Integer> {
- /// Returns `auto`
- #[inline]
- pub fn auto() -> Self {
- ZIndex::Auto
- }
-
- /// Returns whether `self` is `auto`.
- #[inline]
- pub fn is_auto(self) -> bool {
- matches!(self, ZIndex::Auto)
- }
-
- /// Returns the integer value if it is an integer, or `auto`.
- #[inline]
- pub fn integer_or(self, auto: Integer) -> Integer {
- match self {
- ZIndex::Integer(n) => n,
- ZIndex::Auto => auto,
- }
- }
-}
-
-/// Ratio or None.
-#[derive(
- Animate,
- Clone,
- ComputeSquaredDistance,
- Copy,
- Debug,
- MallocSizeOf,
- PartialEq,
- SpecifiedValueInfo,
- ToComputedValue,
- ToCss,
- ToResolvedValue,
- ToShmem,
-)]
-#[repr(C, u8)]
-pub enum PreferredRatio<N> {
- /// Without specified ratio
- #[css(skip)]
- None,
- /// With specified ratio
- Ratio(
- #[animation(field_bound)]
- #[css(field_bound)]
- #[distance(field_bound)]
- Ratio<N>,
- ),
-}
-
-/// A generic value for the `aspect-ratio` property, the value is `auto || <ratio>`.
-#[derive(
- Animate,
- Clone,
- ComputeSquaredDistance,
- Copy,
- Debug,
- MallocSizeOf,
- PartialEq,
- SpecifiedValueInfo,
- ToComputedValue,
- ToCss,
- ToResolvedValue,
- ToShmem,
-)]
-#[repr(C)]
-pub struct GenericAspectRatio<N> {
- /// Specifiy auto or not.
- #[animation(constant)]
- #[css(represents_keyword)]
- pub auto: bool,
- /// The preferred aspect-ratio value.
- #[animation(field_bound)]
- #[css(field_bound)]
- #[distance(field_bound)]
- pub ratio: PreferredRatio<N>,
-}
-
-pub use self::GenericAspectRatio as AspectRatio;
-
-impl<N> AspectRatio<N> {
- /// Returns `auto`
- #[inline]
- pub fn auto() -> Self {
- AspectRatio {
- auto: true,
- ratio: PreferredRatio::None,
- }
- }
-}
-
-impl<N> ToAnimatedZero for AspectRatio<N> {
- #[inline]
- fn to_animated_zero(&self) -> Result<Self, ()> {
- Err(())
- }
-}
diff --git a/components/style/values/generics/ratio.rs b/components/style/values/generics/ratio.rs
deleted file mode 100644
index 8c66fed6026..00000000000
--- a/components/style/values/generics/ratio.rs
+++ /dev/null
@@ -1,50 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
-
-//! Generic types for CSS values related to <ratio>.
-//! https://drafts.csswg.org/css-values/#ratios
-
-use std::fmt::{self, Write};
-use style_traits::{CssWriter, ToCss};
-
-/// A generic value for the `<ratio>` value.
-#[derive(
- Clone,
- Copy,
- Debug,
- MallocSizeOf,
- PartialEq,
- SpecifiedValueInfo,
- ToComputedValue,
- ToResolvedValue,
- ToShmem,
-)]
-#[repr(C)]
-pub struct Ratio<N>(pub N, pub N);
-
-impl<N> ToCss for Ratio<N>
-where
- N: ToCss,
-{
- fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
- where
- W: Write,
- {
- self.0.to_css(dest)?;
- // Even though 1 could be omitted, we don't per
- // https://drafts.csswg.org/css-values-4/#ratio-value:
- //
- // The second <number> is optional, defaulting to 1. However,
- // <ratio> is always serialized with both components.
- //
- // And for compat reasons, see bug 1669742.
- //
- // We serialize with spaces for consistency with all other
- // slash-delimited things, see
- // https://github.com/w3c/csswg-drafts/issues/4282
- dest.write_str(" / ")?;
- self.1.to_css(dest)?;
- Ok(())
- }
-}
diff --git a/components/style/values/generics/rect.rs b/components/style/values/generics/rect.rs
deleted file mode 100644
index e6358373d66..00000000000
--- a/components/style/values/generics/rect.rs
+++ /dev/null
@@ -1,126 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
-
-//! Generic types for CSS values that are composed of four sides.
-
-use crate::parser::{Parse, ParserContext};
-use cssparser::Parser;
-use std::fmt::{self, Write};
-use style_traits::{CssWriter, ParseError, ToCss};
-
-/// A CSS value made of four components, where its `ToCss` impl will try to
-/// serialize as few components as possible, like for example in `border-width`.
-#[derive(
- Animate,
- Clone,
- ComputeSquaredDistance,
- Copy,
- Debug,
- MallocSizeOf,
- PartialEq,
- SpecifiedValueInfo,
- ToAnimatedValue,
- ToAnimatedZero,
- ToComputedValue,
- ToResolvedValue,
- ToShmem,
-)]
-#[repr(C)]
-pub struct Rect<T>(pub T, pub T, pub T, pub T);
-
-impl<T> Rect<T> {
- /// Returns a new `Rect<T>` value.
- pub fn new(first: T, second: T, third: T, fourth: T) -> Self {
- Rect(first, second, third, fourth)
- }
-}
-
-impl<T> Rect<T>
-where
- T: Clone,
-{
- /// Returns a rect with all the values equal to `v`.
- pub fn all(v: T) -> Self {
- Rect::new(v.clone(), v.clone(), v.clone(), v)
- }
-
- /// Parses a new `Rect<T>` value with the given parse function.
- pub fn parse_with<'i, 't, Parse>(
- context: &ParserContext,
- input: &mut Parser<'i, 't>,
- parse: Parse,
- ) -> Result<Self, ParseError<'i>>
- where
- Parse: Fn(&ParserContext, &mut Parser<'i, 't>) -> Result<T, ParseError<'i>>,
- {
- let first = parse(context, input)?;
- let second = if let Ok(second) = input.try_parse(|i| parse(context, i)) {
- second
- } else {
- // <first>
- return Ok(Self::new(
- first.clone(),
- first.clone(),
- first.clone(),
- first,
- ));
- };
- let third = if let Ok(third) = input.try_parse(|i| parse(context, i)) {
- third
- } else {
- // <first> <second>
- return Ok(Self::new(first.clone(), second.clone(), first, second));
- };
- let fourth = if let Ok(fourth) = input.try_parse(|i| parse(context, i)) {
- fourth
- } else {
- // <first> <second> <third>
- return Ok(Self::new(first, second.clone(), third, second));
- };
- // <first> <second> <third> <fourth>
- Ok(Self::new(first, second, third, fourth))
- }
-}
-
-impl<T> Parse for Rect<T>
-where
- T: Clone + Parse,
-{
- #[inline]
- fn parse<'i, 't>(
- context: &ParserContext,
- input: &mut Parser<'i, 't>,
- ) -> Result<Self, ParseError<'i>> {
- Self::parse_with(context, input, T::parse)
- }
-}
-
-impl<T> ToCss for Rect<T>
-where
- T: PartialEq + ToCss,
-{
- fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
- where
- W: Write,
- {
- self.0.to_css(dest)?;
- let same_vertical = self.0 == self.2;
- let same_horizontal = self.1 == self.3;
- if same_vertical && same_horizontal && self.0 == self.1 {
- return Ok(());
- }
- dest.write_char(' ')?;
- self.1.to_css(dest)?;
- if same_vertical && same_horizontal {
- return Ok(());
- }
- dest.write_char(' ')?;
- self.2.to_css(dest)?;
- if same_horizontal {
- return Ok(());
- }
- dest.write_char(' ')?;
- self.3.to_css(dest)
- }
-}
diff --git a/components/style/values/generics/size.rs b/components/style/values/generics/size.rs
deleted file mode 100644
index 0027245e488..00000000000
--- a/components/style/values/generics/size.rs
+++ /dev/null
@@ -1,99 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
-
-//! Generic type for CSS properties that are composed by two dimensions.
-
-use crate::parser::ParserContext;
-use crate::Zero;
-use cssparser::Parser;
-use std::fmt::{self, Write};
-use style_traits::{CssWriter, ParseError, ToCss};
-
-/// A generic size, for `border-*-radius` longhand properties, or
-/// `border-spacing`.
-#[derive(
- Animate,
- Clone,
- ComputeSquaredDistance,
- Copy,
- Debug,
- MallocSizeOf,
- PartialEq,
- SpecifiedValueInfo,
- ToAnimatedZero,
- ToAnimatedValue,
- ToComputedValue,
- ToResolvedValue,
- ToShmem,
-)]
-#[allow(missing_docs)]
-#[repr(C)]
-pub struct Size2D<L> {
- pub width: L,
- pub height: L,
-}
-
-impl<L> Size2D<L> {
- #[inline]
- /// Create a new `Size2D` for an area of given width and height.
- pub fn new(width: L, height: L) -> Self {
- Self { width, height }
- }
-
- /// Returns the width component.
- pub fn width(&self) -> &L {
- &self.width
- }
-
- /// Returns the height component.
- pub fn height(&self) -> &L {
- &self.height
- }
-
- /// Parse a `Size2D` with a given parsing function.
- pub fn parse_with<'i, 't, F>(
- context: &ParserContext,
- input: &mut Parser<'i, 't>,
- parse_one: F,
- ) -> Result<Self, ParseError<'i>>
- where
- L: Clone,
- F: Fn(&ParserContext, &mut Parser<'i, 't>) -> Result<L, ParseError<'i>>,
- {
- let first = parse_one(context, input)?;
- let second = input
- .try_parse(|i| parse_one(context, i))
- .unwrap_or_else(|_| first.clone());
- Ok(Self::new(first, second))
- }
-}
-
-impl<L> ToCss for Size2D<L>
-where
- L: ToCss + PartialEq,
-{
- fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
- where
- W: Write,
- {
- self.width.to_css(dest)?;
-
- if self.height != self.width {
- dest.write_char(' ')?;
- self.height.to_css(dest)?;
- }
-
- Ok(())
- }
-}
-
-impl<L: Zero> Zero for Size2D<L> {
- fn zero() -> Self {
- Self::new(L::zero(), L::zero())
- }
-
- fn is_zero(&self) -> bool {
- self.width.is_zero() && self.height.is_zero()
- }
-}
diff --git a/components/style/values/generics/svg.rs b/components/style/values/generics/svg.rs
deleted file mode 100644
index 43ba77f1ff4..00000000000
--- a/components/style/values/generics/svg.rs
+++ /dev/null
@@ -1,221 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
-
-//! Generic types for CSS values in SVG
-
-use crate::parser::{Parse, ParserContext};
-use cssparser::Parser;
-use style_traits::ParseError;
-
-/// The fallback of an SVG paint server value.
-#[derive(
- Animate,
- Clone,
- ComputeSquaredDistance,
- Debug,
- MallocSizeOf,
- PartialEq,
- Parse,
- SpecifiedValueInfo,
- ToAnimatedValue,
- ToAnimatedZero,
- ToComputedValue,
- ToCss,
- ToResolvedValue,
- ToShmem,
-)]
-#[repr(C, u8)]
-pub enum GenericSVGPaintFallback<C> {
- /// The `none` keyword.
- None,
- /// A magic value that represents no fallback specified and serializes to
- /// the empty string.
- #[css(skip)]
- Unset,
- /// A color.
- Color(C),
-}
-
-pub use self::GenericSVGPaintFallback as SVGPaintFallback;
-
-/// An SVG paint value
-///
-/// <https://www.w3.org/TR/SVG2/painting.html#SpecifyingPaint>
-#[derive(
- Animate,
- Clone,
- ComputeSquaredDistance,
- Debug,
- MallocSizeOf,
- PartialEq,
- SpecifiedValueInfo,
- ToAnimatedValue,
- ToAnimatedZero,
- ToComputedValue,
- ToCss,
- ToResolvedValue,
- ToShmem,
-)]
-#[animation(no_bound(Url))]
-#[repr(C)]
-pub struct GenericSVGPaint<Color, Url> {
- /// The paint source.
- pub kind: GenericSVGPaintKind<Color, Url>,
- /// The fallback color.
- pub fallback: GenericSVGPaintFallback<Color>,
-}
-
-pub use self::GenericSVGPaint as SVGPaint;
-
-impl<C, U> Default for SVGPaint<C, U> {
- fn default() -> Self {
- Self {
- kind: SVGPaintKind::None,
- fallback: SVGPaintFallback::Unset,
- }
- }
-}
-
-/// An SVG paint value without the fallback.
-///
-/// Whereas the spec only allows PaintServer to have a fallback, Gecko lets the
-/// context properties have a fallback as well.
-#[derive(
- Animate,
- Clone,
- ComputeSquaredDistance,
- Debug,
- MallocSizeOf,
- PartialEq,
- Parse,
- SpecifiedValueInfo,
- ToAnimatedValue,
- ToAnimatedZero,
- ToComputedValue,
- ToCss,
- ToResolvedValue,
- ToShmem,
-)]
-#[animation(no_bound(U))]
-#[repr(C, u8)]
-pub enum GenericSVGPaintKind<C, U> {
- /// `none`
- #[animation(error)]
- None,
- /// `<color>`
- Color(C),
- /// `url(...)`
- #[animation(error)]
- PaintServer(U),
- /// `context-fill`
- ContextFill,
- /// `context-stroke`
- ContextStroke,
-}
-
-pub use self::GenericSVGPaintKind as SVGPaintKind;
-
-impl<C: Parse, U: Parse> Parse for SVGPaint<C, U> {
- fn parse<'i, 't>(
- context: &ParserContext,
- input: &mut Parser<'i, 't>,
- ) -> Result<Self, ParseError<'i>> {
- let kind = SVGPaintKind::parse(context, input)?;
- if matches!(kind, SVGPaintKind::None | SVGPaintKind::Color(..)) {
- return Ok(SVGPaint {
- kind,
- fallback: SVGPaintFallback::Unset,
- });
- }
- let fallback = input
- .try_parse(|i| SVGPaintFallback::parse(context, i))
- .unwrap_or(SVGPaintFallback::Unset);
- Ok(SVGPaint { kind, fallback })
- }
-}
-
-/// An SVG length value supports `context-value` in addition to length.
-#[derive(
- Animate,
- Clone,
- ComputeSquaredDistance,
- Copy,
- Debug,
- MallocSizeOf,
- PartialEq,
- SpecifiedValueInfo,
- ToAnimatedValue,
- ToAnimatedZero,
- ToComputedValue,
- ToCss,
- ToResolvedValue,
- ToShmem,
-)]
-#[repr(C, u8)]
-pub enum GenericSVGLength<L> {
- /// `<length> | <percentage> | <number>`
- LengthPercentage(L),
- /// `context-value`
- #[animation(error)]
- ContextValue,
-}
-
-pub use self::GenericSVGLength as SVGLength;
-
-/// Generic value for stroke-dasharray.
-#[derive(
- Clone,
- Debug,
- MallocSizeOf,
- PartialEq,
- SpecifiedValueInfo,
- ToAnimatedValue,
- ToAnimatedZero,
- ToComputedValue,
- ToCss,
- ToResolvedValue,
- ToShmem,
-)]
-#[repr(C, u8)]
-pub enum GenericSVGStrokeDashArray<L> {
- /// `[ <length> | <percentage> | <number> ]#`
- #[css(comma)]
- Values(#[css(if_empty = "none", iterable)] crate::OwnedSlice<L>),
- /// `context-value`
- ContextValue,
-}
-
-pub use self::GenericSVGStrokeDashArray as SVGStrokeDashArray;
-
-/// An SVG opacity value accepts `context-{fill,stroke}-opacity` in
-/// addition to opacity value.
-#[derive(
- Animate,
- Clone,
- ComputeSquaredDistance,
- Copy,
- Debug,
- MallocSizeOf,
- PartialEq,
- Parse,
- SpecifiedValueInfo,
- ToAnimatedZero,
- ToComputedValue,
- ToCss,
- ToResolvedValue,
- ToShmem,
-)]
-#[repr(C, u8)]
-pub enum GenericSVGOpacity<OpacityType> {
- /// `<opacity-value>`
- Opacity(OpacityType),
- /// `context-fill-opacity`
- #[animation(error)]
- ContextFillOpacity,
- /// `context-stroke-opacity`
- #[animation(error)]
- ContextStrokeOpacity,
-}
-
-pub use self::GenericSVGOpacity as SVGOpacity;
diff --git a/components/style/values/generics/text.rs b/components/style/values/generics/text.rs
deleted file mode 100644
index 9b59ff0dc3f..00000000000
--- a/components/style/values/generics/text.rs
+++ /dev/null
@@ -1,156 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
-
-//! Generic types for text properties.
-
-use crate::parser::ParserContext;
-use crate::values::animated::ToAnimatedZero;
-use cssparser::Parser;
-use style_traits::ParseError;
-
-/// A generic value for the `initial-letter` property.
-#[derive(
- Clone,
- Copy,
- Debug,
- MallocSizeOf,
- PartialEq,
- SpecifiedValueInfo,
- ToComputedValue,
- ToCss,
- ToResolvedValue,
- ToShmem,
-)]
-pub enum InitialLetter<Number, Integer> {
- /// `normal`
- Normal,
- /// `<number> <integer>?`
- Specified(Number, Option<Integer>),
-}
-
-impl<N, I> InitialLetter<N, I> {
- /// Returns `normal`.
- #[inline]
- pub fn normal() -> Self {
- InitialLetter::Normal
- }
-}
-
-/// A generic spacing value for the `letter-spacing` and `word-spacing` properties.
-#[derive(Clone, Copy, Debug, MallocSizeOf, PartialEq, SpecifiedValueInfo, ToCss, ToShmem)]
-pub enum Spacing<Value> {
- /// `normal`
- Normal,
- /// `<value>`
- Value(Value),
-}
-
-impl<Value> Spacing<Value> {
- /// Returns `normal`.
- #[inline]
- pub fn normal() -> Self {
- Spacing::Normal
- }
-
- /// Parses.
- #[inline]
- pub fn parse_with<'i, 't, F>(
- context: &ParserContext,
- input: &mut Parser<'i, 't>,
- parse: F,
- ) -> Result<Self, ParseError<'i>>
- where
- F: FnOnce(&ParserContext, &mut Parser<'i, 't>) -> Result<Value, ParseError<'i>>,
- {
- if input
- .try_parse(|i| i.expect_ident_matching("normal"))
- .is_ok()
- {
- return Ok(Spacing::Normal);
- }
- parse(context, input).map(Spacing::Value)
- }
-}
-
-#[cfg(feature = "gecko")]
-fn line_height_moz_block_height_enabled(context: &ParserContext) -> bool {
- context.in_ua_sheet() ||
- static_prefs::pref!("layout.css.line-height-moz-block-height.content.enabled")
-}
-
-/// A generic value for the `line-height` property.
-#[derive(
- Animate,
- Clone,
- ComputeSquaredDistance,
- Copy,
- Debug,
- MallocSizeOf,
- PartialEq,
- SpecifiedValueInfo,
- ToAnimatedValue,
- ToCss,
- ToShmem,
- Parse,
-)]
-#[repr(C, u8)]
-pub enum GenericLineHeight<N, L> {
- /// `normal`
- Normal,
- /// `-moz-block-height`
- #[cfg(feature = "gecko")]
- #[parse(condition = "line_height_moz_block_height_enabled")]
- MozBlockHeight,
- /// `<number>`
- Number(N),
- /// `<length-percentage>`
- Length(L),
-}
-
-pub use self::GenericLineHeight as LineHeight;
-
-impl<N, L> ToAnimatedZero for LineHeight<N, L> {
- #[inline]
- fn to_animated_zero(&self) -> Result<Self, ()> {
- Err(())
- }
-}
-
-impl<N, L> LineHeight<N, L> {
- /// Returns `normal`.
- #[inline]
- pub fn normal() -> Self {
- LineHeight::Normal
- }
-}
-
-/// Implements type for text-decoration-thickness
-/// which takes the grammar of auto | from-font | <length> | <percentage>
-///
-/// https://drafts.csswg.org/css-text-decor-4/
-#[repr(C, u8)]
-#[cfg_attr(feature = "servo", derive(Deserialize, Serialize))]
-#[derive(
- Animate,
- Clone,
- Copy,
- ComputeSquaredDistance,
- ToAnimatedZero,
- Debug,
- Eq,
- MallocSizeOf,
- Parse,
- PartialEq,
- SpecifiedValueInfo,
- ToComputedValue,
- ToCss,
- ToResolvedValue,
- ToShmem,
-)]
-#[allow(missing_docs)]
-pub enum GenericTextDecorationLength<L> {
- LengthPercentage(L),
- Auto,
- FromFont,
-}
diff --git a/components/style/values/generics/transform.rs b/components/style/values/generics/transform.rs
deleted file mode 100644
index f1fd062b1ee..00000000000
--- a/components/style/values/generics/transform.rs
+++ /dev/null
@@ -1,886 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
-
-//! Generic types for CSS values that are related to transformations.
-
-use crate::values::computed::length::Length as ComputedLength;
-use crate::values::computed::length::LengthPercentage as ComputedLengthPercentage;
-use crate::values::specified::angle::Angle as SpecifiedAngle;
-use crate::values::specified::length::Length as SpecifiedLength;
-use crate::values::specified::length::LengthPercentage as SpecifiedLengthPercentage;
-use crate::values::{computed, CSSFloat};
-use crate::{Zero, ZeroNoPercent};
-use euclid;
-use euclid::default::{Rect, Transform3D};
-use std::fmt::{self, Write};
-use style_traits::{CssWriter, ToCss};
-
-/// A generic 2D transformation matrix.
-#[allow(missing_docs)]
-#[derive(
- Clone,
- Copy,
- Debug,
- Deserialize,
- MallocSizeOf,
- PartialEq,
- Serialize,
- SpecifiedValueInfo,
- ToComputedValue,
- ToCss,
- ToResolvedValue,
- ToShmem,
-)]
-#[css(comma, function = "matrix")]
-#[repr(C)]
-pub struct GenericMatrix<T> {
- pub a: T,
- pub b: T,
- pub c: T,
- pub d: T,
- pub e: T,
- pub f: T,
-}
-
-pub use self::GenericMatrix as Matrix;
-
-#[allow(missing_docs)]
-#[cfg_attr(rustfmt, rustfmt_skip)]
-#[derive(
- Clone,
- Copy,
- Debug,
- Deserialize,
- MallocSizeOf,
- PartialEq,
- Serialize,
- SpecifiedValueInfo,
- ToComputedValue,
- ToCss,
- ToResolvedValue,
- ToShmem,
-)]
-#[css(comma, function = "matrix3d")]
-#[repr(C)]
-pub struct GenericMatrix3D<T> {
- pub m11: T, pub m12: T, pub m13: T, pub m14: T,
- pub m21: T, pub m22: T, pub m23: T, pub m24: T,
- pub m31: T, pub m32: T, pub m33: T, pub m34: T,
- pub m41: T, pub m42: T, pub m43: T, pub m44: T,
-}
-
-pub use self::GenericMatrix3D as Matrix3D;
-
-#[cfg_attr(rustfmt, rustfmt_skip)]
-impl<T: Into<f64>> From<Matrix<T>> for Transform3D<f64> {
- #[inline]
- fn from(m: Matrix<T>) -> Self {
- Transform3D::new(
- m.a.into(), m.b.into(), 0.0, 0.0,
- m.c.into(), m.d.into(), 0.0, 0.0,
- 0.0, 0.0, 1.0, 0.0,
- m.e.into(), m.f.into(), 0.0, 1.0,
- )
- }
-}
-
-#[cfg_attr(rustfmt, rustfmt_skip)]
-impl<T: Into<f64>> From<Matrix3D<T>> for Transform3D<f64> {
- #[inline]
- fn from(m: Matrix3D<T>) -> Self {
- Transform3D::new(
- m.m11.into(), m.m12.into(), m.m13.into(), m.m14.into(),
- m.m21.into(), m.m22.into(), m.m23.into(), m.m24.into(),
- m.m31.into(), m.m32.into(), m.m33.into(), m.m34.into(),
- m.m41.into(), m.m42.into(), m.m43.into(), m.m44.into(),
- )
- }
-}
-
-/// A generic transform origin.
-#[derive(
- Animate,
- Clone,
- ComputeSquaredDistance,
- Copy,
- Debug,
- MallocSizeOf,
- PartialEq,
- SpecifiedValueInfo,
- ToAnimatedZero,
- ToComputedValue,
- ToCss,
- ToResolvedValue,
- ToShmem,
-)]
-#[repr(C)]
-pub struct GenericTransformOrigin<H, V, Depth> {
- /// The horizontal origin.
- pub horizontal: H,
- /// The vertical origin.
- pub vertical: V,
- /// The depth.
- pub depth: Depth,
-}
-
-pub use self::GenericTransformOrigin as TransformOrigin;
-
-impl<H, V, D> TransformOrigin<H, V, D> {
- /// Returns a new transform origin.
- pub fn new(horizontal: H, vertical: V, depth: D) -> Self {
- Self {
- horizontal,
- vertical,
- depth,
- }
- }
-}
-
-fn is_same<N: PartialEq>(x: &N, y: &N) -> bool {
- x == y
-}
-
-/// A value for the `perspective()` transform function, which is either a
-/// non-negative `<length>` or `none`.
-#[derive(
- Clone,
- Debug,
- Deserialize,
- MallocSizeOf,
- PartialEq,
- Serialize,
- SpecifiedValueInfo,
- ToComputedValue,
- ToCss,
- ToResolvedValue,
- ToShmem,
-)]
-#[repr(C, u8)]
-pub enum GenericPerspectiveFunction<L> {
- /// `none`
- None,
- /// A `<length>`.
- Length(L),
-}
-
-impl<L> GenericPerspectiveFunction<L> {
- /// Returns `f32::INFINITY` or the result of a function on the length value.
- pub fn infinity_or(&self, f: impl FnOnce(&L) -> f32) -> f32 {
- match *self {
- Self::None => f32::INFINITY,
- Self::Length(ref l) => f(l),
- }
- }
-}
-
-pub use self::GenericPerspectiveFunction as PerspectiveFunction;
-
-#[derive(
- Clone,
- Debug,
- Deserialize,
- MallocSizeOf,
- PartialEq,
- Serialize,
- SpecifiedValueInfo,
- ToComputedValue,
- ToCss,
- ToResolvedValue,
- ToShmem,
-)]
-#[repr(C, u8)]
-/// A single operation in the list of a `transform` value
-pub enum GenericTransformOperation<Angle, Number, Length, Integer, LengthPercentage>
-where
- Angle: Zero,
- LengthPercentage: Zero + ZeroNoPercent,
- Number: PartialEq,
-{
- /// Represents a 2D 2x3 matrix.
- Matrix(GenericMatrix<Number>),
- /// Represents a 3D 4x4 matrix.
- Matrix3D(GenericMatrix3D<Number>),
- /// A 2D skew.
- ///
- /// If the second angle is not provided it is assumed zero.
- ///
- /// Syntax can be skew(angle) or skew(angle, angle)
- #[css(comma, function)]
- Skew(Angle, #[css(skip_if = "Zero::is_zero")] Angle),
- /// skewX(angle)
- #[css(function = "skewX")]
- SkewX(Angle),
- /// skewY(angle)
- #[css(function = "skewY")]
- SkewY(Angle),
- /// translate(x, y) or translate(x)
- #[css(comma, function)]
- Translate(
- LengthPercentage,
- #[css(skip_if = "ZeroNoPercent::is_zero_no_percent")] LengthPercentage,
- ),
- /// translateX(x)
- #[css(function = "translateX")]
- TranslateX(LengthPercentage),
- /// translateY(y)
- #[css(function = "translateY")]
- TranslateY(LengthPercentage),
- /// translateZ(z)
- #[css(function = "translateZ")]
- TranslateZ(Length),
- /// translate3d(x, y, z)
- #[css(comma, function = "translate3d")]
- Translate3D(LengthPercentage, LengthPercentage, Length),
- /// A 2D scaling factor.
- ///
- /// Syntax can be scale(factor) or scale(factor, factor)
- #[css(comma, function)]
- Scale(Number, #[css(contextual_skip_if = "is_same")] Number),
- /// scaleX(factor)
- #[css(function = "scaleX")]
- ScaleX(Number),
- /// scaleY(factor)
- #[css(function = "scaleY")]
- ScaleY(Number),
- /// scaleZ(factor)
- #[css(function = "scaleZ")]
- ScaleZ(Number),
- /// scale3D(factorX, factorY, factorZ)
- #[css(comma, function = "scale3d")]
- Scale3D(Number, Number, Number),
- /// Describes a 2D Rotation.
- ///
- /// In a 3D scene `rotate(angle)` is equivalent to `rotateZ(angle)`.
- #[css(function)]
- Rotate(Angle),
- /// Rotation in 3D space around the x-axis.
- #[css(function = "rotateX")]
- RotateX(Angle),
- /// Rotation in 3D space around the y-axis.
- #[css(function = "rotateY")]
- RotateY(Angle),
- /// Rotation in 3D space around the z-axis.
- #[css(function = "rotateZ")]
- RotateZ(Angle),
- /// Rotation in 3D space.
- ///
- /// Generalization of rotateX, rotateY and rotateZ.
- #[css(comma, function = "rotate3d")]
- Rotate3D(Number, Number, Number, Angle),
- /// Specifies a perspective projection matrix.
- ///
- /// Part of CSS Transform Module Level 2 and defined at
- /// [§ 13.1. 3D Transform Function](https://drafts.csswg.org/css-transforms-2/#funcdef-perspective).
- ///
- /// The value must be greater than or equal to zero.
- #[css(function)]
- Perspective(GenericPerspectiveFunction<Length>),
- /// A intermediate type for interpolation of mismatched transform lists.
- #[allow(missing_docs)]
- #[css(comma, function = "interpolatematrix")]
- InterpolateMatrix {
- from_list: GenericTransform<
- GenericTransformOperation<Angle, Number, Length, Integer, LengthPercentage>,
- >,
- to_list: GenericTransform<
- GenericTransformOperation<Angle, Number, Length, Integer, LengthPercentage>,
- >,
- progress: computed::Percentage,
- },
- /// A intermediate type for accumulation of mismatched transform lists.
- #[allow(missing_docs)]
- #[css(comma, function = "accumulatematrix")]
- AccumulateMatrix {
- from_list: GenericTransform<
- GenericTransformOperation<Angle, Number, Length, Integer, LengthPercentage>,
- >,
- to_list: GenericTransform<
- GenericTransformOperation<Angle, Number, Length, Integer, LengthPercentage>,
- >,
- count: Integer,
- },
-}
-
-pub use self::GenericTransformOperation as TransformOperation;
-
-#[derive(
- Clone,
- Debug,
- Deserialize,
- MallocSizeOf,
- PartialEq,
- Serialize,
- SpecifiedValueInfo,
- ToComputedValue,
- ToCss,
- ToResolvedValue,
- ToShmem,
-)]
-#[repr(C)]
-/// A value of the `transform` property
-pub struct GenericTransform<T>(#[css(if_empty = "none", iterable)] pub crate::OwnedSlice<T>);
-
-pub use self::GenericTransform as Transform;
-
-impl<Angle, Number, Length, Integer, LengthPercentage>
- TransformOperation<Angle, Number, Length, Integer, LengthPercentage>
-where
- Angle: Zero,
- LengthPercentage: Zero + ZeroNoPercent,
- Number: PartialEq,
-{
- /// Check if it is any rotate function.
- pub fn is_rotate(&self) -> bool {
- use self::TransformOperation::*;
- matches!(
- *self,
- Rotate(..) | Rotate3D(..) | RotateX(..) | RotateY(..) | RotateZ(..)
- )
- }
-
- /// Check if it is any translate function
- pub fn is_translate(&self) -> bool {
- use self::TransformOperation::*;
- match *self {
- Translate(..) | Translate3D(..) | TranslateX(..) | TranslateY(..) | TranslateZ(..) => {
- true
- },
- _ => false,
- }
- }
-
- /// Check if it is any scale function
- pub fn is_scale(&self) -> bool {
- use self::TransformOperation::*;
- match *self {
- Scale(..) | Scale3D(..) | ScaleX(..) | ScaleY(..) | ScaleZ(..) => true,
- _ => false,
- }
- }
-}
-
-/// Convert a length type into the absolute lengths.
-pub trait ToAbsoluteLength {
- /// Returns the absolute length as pixel value.
- fn to_pixel_length(&self, containing_len: Option<ComputedLength>) -> Result<CSSFloat, ()>;
-}
-
-impl ToAbsoluteLength for SpecifiedLength {
- // This returns Err(()) if there is any relative length or percentage. We use this when
- // parsing a transform list of DOMMatrix because we want to return a DOM Exception
- // if there is relative length.
- #[inline]
- fn to_pixel_length(&self, _containing_len: Option<ComputedLength>) -> Result<CSSFloat, ()> {
- match *self {
- SpecifiedLength::NoCalc(len) => len.to_computed_pixel_length_without_context(),
- SpecifiedLength::Calc(ref calc) => calc.to_computed_pixel_length_without_context(),
- }
- }
-}
-
-impl ToAbsoluteLength for SpecifiedLengthPercentage {
- // This returns Err(()) if there is any relative length or percentage. We use this when
- // parsing a transform list of DOMMatrix because we want to return a DOM Exception
- // if there is relative length.
- #[inline]
- fn to_pixel_length(&self, _containing_len: Option<ComputedLength>) -> Result<CSSFloat, ()> {
- use self::SpecifiedLengthPercentage::*;
- match *self {
- Length(len) => len.to_computed_pixel_length_without_context(),
- Calc(ref calc) => calc.to_computed_pixel_length_without_context(),
- Percentage(..) => Err(()),
- }
- }
-}
-
-impl ToAbsoluteLength for ComputedLength {
- #[inline]
- fn to_pixel_length(&self, _containing_len: Option<ComputedLength>) -> Result<CSSFloat, ()> {
- Ok(self.px())
- }
-}
-
-impl ToAbsoluteLength for ComputedLengthPercentage {
- #[inline]
- fn to_pixel_length(&self, containing_len: Option<ComputedLength>) -> Result<CSSFloat, ()> {
- Ok(self
- .maybe_percentage_relative_to(containing_len)
- .ok_or(())?
- .px())
- }
-}
-
-/// Support the conversion to a 3d matrix.
-pub trait ToMatrix {
- /// Check if it is a 3d transform function.
- fn is_3d(&self) -> bool;
-
- /// Return the equivalent 3d matrix.
- fn to_3d_matrix(
- &self,
- reference_box: Option<&Rect<ComputedLength>>,
- ) -> Result<Transform3D<f64>, ()>;
-}
-
-/// A little helper to deal with both specified and computed angles.
-pub trait ToRadians {
- /// Return the radians value as a 64-bit floating point value.
- fn radians64(&self) -> f64;
-}
-
-impl ToRadians for computed::angle::Angle {
- #[inline]
- fn radians64(&self) -> f64 {
- computed::angle::Angle::radians64(self)
- }
-}
-
-impl ToRadians for SpecifiedAngle {
- #[inline]
- fn radians64(&self) -> f64 {
- computed::angle::Angle::from_degrees(self.degrees()).radians64()
- }
-}
-
-impl<Angle, Number, Length, Integer, LoP> ToMatrix
- for TransformOperation<Angle, Number, Length, Integer, LoP>
-where
- Angle: Zero + ToRadians + Copy,
- Number: PartialEq + Copy + Into<f32> + Into<f64>,
- Length: ToAbsoluteLength,
- LoP: Zero + ToAbsoluteLength + ZeroNoPercent,
-{
- #[inline]
- fn is_3d(&self) -> bool {
- use self::TransformOperation::*;
- match *self {
- Translate3D(..) | TranslateZ(..) | Rotate3D(..) | RotateX(..) | RotateY(..) |
- RotateZ(..) | Scale3D(..) | ScaleZ(..) | Perspective(..) | Matrix3D(..) => true,
- _ => false,
- }
- }
-
- /// If |reference_box| is None, we will drop the percent part from translate because
- /// we cannot resolve it without the layout info, for computed TransformOperation.
- /// However, for specified TransformOperation, we will return Err(()) if there is any relative
- /// lengths because the only caller, DOMMatrix, doesn't accept relative lengths.
- #[inline]
- fn to_3d_matrix(
- &self,
- reference_box: Option<&Rect<ComputedLength>>,
- ) -> Result<Transform3D<f64>, ()> {
- use self::TransformOperation::*;
-
- let reference_width = reference_box.map(|v| v.size.width);
- let reference_height = reference_box.map(|v| v.size.height);
- let matrix = match *self {
- Rotate3D(ax, ay, az, theta) => {
- let theta = theta.radians64();
- let (ax, ay, az, theta) =
- get_normalized_vector_and_angle(ax.into(), ay.into(), az.into(), theta);
- Transform3D::rotation(
- ax as f64,
- ay as f64,
- az as f64,
- euclid::Angle::radians(theta),
- )
- },
- RotateX(theta) => {
- let theta = euclid::Angle::radians(theta.radians64());
- Transform3D::rotation(1., 0., 0., theta)
- },
- RotateY(theta) => {
- let theta = euclid::Angle::radians(theta.radians64());
- Transform3D::rotation(0., 1., 0., theta)
- },
- RotateZ(theta) | Rotate(theta) => {
- let theta = euclid::Angle::radians(theta.radians64());
- Transform3D::rotation(0., 0., 1., theta)
- },
- Perspective(ref p) => {
- let px = match p {
- PerspectiveFunction::None => f32::INFINITY,
- PerspectiveFunction::Length(ref p) => p.to_pixel_length(None)?,
- };
- create_perspective_matrix(px).cast()
- },
- Scale3D(sx, sy, sz) => Transform3D::scale(sx.into(), sy.into(), sz.into()),
- Scale(sx, sy) => Transform3D::scale(sx.into(), sy.into(), 1.),
- ScaleX(s) => Transform3D::scale(s.into(), 1., 1.),
- ScaleY(s) => Transform3D::scale(1., s.into(), 1.),
- ScaleZ(s) => Transform3D::scale(1., 1., s.into()),
- Translate3D(ref tx, ref ty, ref tz) => {
- let tx = tx.to_pixel_length(reference_width)? as f64;
- let ty = ty.to_pixel_length(reference_height)? as f64;
- Transform3D::translation(tx, ty, tz.to_pixel_length(None)? as f64)
- },
- Translate(ref tx, ref ty) => {
- let tx = tx.to_pixel_length(reference_width)? as f64;
- let ty = ty.to_pixel_length(reference_height)? as f64;
- Transform3D::translation(tx, ty, 0.)
- },
- TranslateX(ref t) => {
- let t = t.to_pixel_length(reference_width)? as f64;
- Transform3D::translation(t, 0., 0.)
- },
- TranslateY(ref t) => {
- let t = t.to_pixel_length(reference_height)? as f64;
- Transform3D::translation(0., t, 0.)
- },
- TranslateZ(ref z) => Transform3D::translation(0., 0., z.to_pixel_length(None)? as f64),
- Skew(theta_x, theta_y) => Transform3D::skew(
- euclid::Angle::radians(theta_x.radians64()),
- euclid::Angle::radians(theta_y.radians64()),
- ),
- SkewX(theta) => Transform3D::skew(
- euclid::Angle::radians(theta.radians64()),
- euclid::Angle::radians(0.),
- ),
- SkewY(theta) => Transform3D::skew(
- euclid::Angle::radians(0.),
- euclid::Angle::radians(theta.radians64()),
- ),
- Matrix3D(m) => m.into(),
- Matrix(m) => m.into(),
- InterpolateMatrix { .. } | AccumulateMatrix { .. } => {
- // TODO: Convert InterpolateMatrix/AccumulateMatrix into a valid Transform3D by
- // the reference box and do interpolation on these two Transform3D matrices.
- // Both Gecko and Servo don't support this for computing distance, and Servo
- // doesn't support animations on InterpolateMatrix/AccumulateMatrix, so
- // return an identity matrix.
- // Note: DOMMatrix doesn't go into this arm.
- Transform3D::identity()
- },
- };
- Ok(matrix)
- }
-}
-
-impl<T> Transform<T> {
- /// `none`
- pub fn none() -> Self {
- Transform(Default::default())
- }
-}
-
-impl<T: ToMatrix> Transform<T> {
- /// Return the equivalent 3d matrix of this transform list.
- ///
- /// We return a pair: the first one is the transform matrix, and the second one
- /// indicates if there is any 3d transform function in this transform list.
- #[cfg_attr(rustfmt, rustfmt_skip)]
- pub fn to_transform_3d_matrix(
- &self,
- reference_box: Option<&Rect<ComputedLength>>
- ) -> Result<(Transform3D<CSSFloat>, bool), ()> {
- Self::components_to_transform_3d_matrix(&self.0, reference_box)
- }
-
- /// Converts a series of components to a 3d matrix.
- #[cfg_attr(rustfmt, rustfmt_skip)]
- pub fn components_to_transform_3d_matrix(
- ops: &[T],
- reference_box: Option<&Rect<ComputedLength>>,
- ) -> Result<(Transform3D<CSSFloat>, bool), ()> {
- let cast_3d_transform = |m: Transform3D<f64>| -> Transform3D<CSSFloat> {
- use std::{f32, f64};
- let cast = |v: f64| v.min(f32::MAX as f64).max(f32::MIN as f64) as f32;
- Transform3D::new(
- cast(m.m11), cast(m.m12), cast(m.m13), cast(m.m14),
- cast(m.m21), cast(m.m22), cast(m.m23), cast(m.m24),
- cast(m.m31), cast(m.m32), cast(m.m33), cast(m.m34),
- cast(m.m41), cast(m.m42), cast(m.m43), cast(m.m44),
- )
- };
-
- let (m, is_3d) = Self::components_to_transform_3d_matrix_f64(ops, reference_box)?;
- Ok((cast_3d_transform(m), is_3d))
- }
-
- /// Same as Transform::to_transform_3d_matrix but a f64 version.
- pub fn to_transform_3d_matrix_f64(
- &self,
- reference_box: Option<&Rect<ComputedLength>>
- ) -> Result<(Transform3D<f64>, bool), ()> {
- Self::components_to_transform_3d_matrix_f64(&self.0, reference_box)
- }
-
- fn components_to_transform_3d_matrix_f64(
- ops: &[T],
- reference_box: Option<&Rect<ComputedLength>>,
- ) -> Result<(Transform3D<f64>, bool), ()> {
- // We intentionally use Transform3D<f64> during computation to avoid
- // error propagation because using f32 to compute triangle functions
- // (e.g. in rotation()) is not accurate enough. In Gecko, we also use
- // "double" to compute the triangle functions. Therefore, let's use
- // Transform3D<f64> during matrix computation and cast it into f32 in
- // the end.
- let mut transform = Transform3D::<f64>::identity();
- let mut contain_3d = false;
-
- for operation in ops {
- let matrix = operation.to_3d_matrix(reference_box)?;
- contain_3d = contain_3d || operation.is_3d();
- transform = matrix.then(&transform);
- }
-
- Ok((transform, contain_3d))
- }
-}
-
-/// Return the transform matrix from a perspective length.
-#[inline]
-pub fn create_perspective_matrix(d: CSSFloat) -> Transform3D<CSSFloat> {
- if d.is_finite() {
- Transform3D::perspective(d.max(1.))
- } else {
- Transform3D::identity()
- }
-}
-
-/// Return the normalized direction vector and its angle for Rotate3D.
-pub fn get_normalized_vector_and_angle<T: Zero>(
- x: CSSFloat,
- y: CSSFloat,
- z: CSSFloat,
- angle: T,
-) -> (CSSFloat, CSSFloat, CSSFloat, T) {
- use crate::values::computed::transform::DirectionVector;
- use euclid::approxeq::ApproxEq;
- let vector = DirectionVector::new(x, y, z);
- if vector.square_length().approx_eq(&f32::zero()) {
- // https://www.w3.org/TR/css-transforms-1/#funcdef-rotate3d
- // A direction vector that cannot be normalized, such as [0, 0, 0], will cause the
- // rotation to not be applied, so we use identity matrix (i.e. rotate3d(0, 0, 1, 0)).
- (0., 0., 1., T::zero())
- } else {
- let vector = vector.robust_normalize();
- (vector.x, vector.y, vector.z, angle)
- }
-}
-
-#[derive(
- Clone,
- Copy,
- Debug,
- Deserialize,
- MallocSizeOf,
- PartialEq,
- Serialize,
- SpecifiedValueInfo,
- ToAnimatedZero,
- ToComputedValue,
- ToResolvedValue,
- ToShmem,
-)]
-#[repr(C, u8)]
-/// A value of the `Rotate` property
-///
-/// <https://drafts.csswg.org/css-transforms-2/#individual-transforms>
-pub enum GenericRotate<Number, Angle> {
- /// 'none'
- None,
- /// '<angle>'
- Rotate(Angle),
- /// '<number>{3} <angle>'
- Rotate3D(Number, Number, Number, Angle),
-}
-
-pub use self::GenericRotate as Rotate;
-
-/// A trait to check if the current 3D vector is parallel to the DirectionVector.
-/// This is especially for serialization on Rotate.
-pub trait IsParallelTo {
- /// Returns true if this is parallel to the vector.
- fn is_parallel_to(&self, vector: &computed::transform::DirectionVector) -> bool;
-}
-
-impl<Number, Angle> ToCss for Rotate<Number, Angle>
-where
- Number: Copy + ToCss + Zero,
- Angle: ToCss,
- (Number, Number, Number): IsParallelTo,
-{
- fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
- where
- W: fmt::Write,
- {
- use crate::values::computed::transform::DirectionVector;
- match *self {
- Rotate::None => dest.write_str("none"),
- Rotate::Rotate(ref angle) => angle.to_css(dest),
- Rotate::Rotate3D(x, y, z, ref angle) => {
- // If the axis is parallel with the x or y axes, it must serialize as the
- // appropriate keyword. If a rotation about the z axis (that is, in 2D) is
- // specified, the property must serialize as just an <angle>
- //
- // https://drafts.csswg.org/css-transforms-2/#individual-transform-serialization
- let v = (x, y, z);
- let axis = if x.is_zero() && y.is_zero() && z.is_zero() {
- // The zero length vector is parallel to every other vector, so
- // is_parallel_to() returns true for it. However, it is definitely different
- // from x axis, y axis, or z axis, and it's meaningless to perform a rotation
- // using that direction vector. So we *have* to serialize it using that same
- // vector - we can't simplify to some theoretically parallel axis-aligned
- // vector.
- None
- } else if v.is_parallel_to(&DirectionVector::new(1., 0., 0.)) {
- Some("x ")
- } else if v.is_parallel_to(&DirectionVector::new(0., 1., 0.)) {
- Some("y ")
- } else if v.is_parallel_to(&DirectionVector::new(0., 0., 1.)) {
- // When we're parallel to the z-axis, we can just serialize the angle.
- return angle.to_css(dest);
- } else {
- None
- };
- match axis {
- Some(a) => dest.write_str(a)?,
- None => {
- x.to_css(dest)?;
- dest.write_char(' ')?;
- y.to_css(dest)?;
- dest.write_char(' ')?;
- z.to_css(dest)?;
- dest.write_char(' ')?;
- },
- }
- angle.to_css(dest)
- },
- }
- }
-}
-
-#[derive(
- Clone,
- Copy,
- Debug,
- Deserialize,
- MallocSizeOf,
- PartialEq,
- Serialize,
- SpecifiedValueInfo,
- ToAnimatedZero,
- ToComputedValue,
- ToResolvedValue,
- ToShmem,
-)]
-#[repr(C, u8)]
-/// A value of the `Scale` property
-///
-/// <https://drafts.csswg.org/css-transforms-2/#individual-transforms>
-pub enum GenericScale<Number> {
- /// 'none'
- None,
- /// '<number>{1,3}'
- Scale(Number, Number, Number),
-}
-
-pub use self::GenericScale as Scale;
-
-impl<Number> ToCss for Scale<Number>
-where
- Number: ToCss + PartialEq + Copy,
- f32: From<Number>,
-{
- fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
- where
- W: fmt::Write,
- f32: From<Number>,
- {
- match *self {
- Scale::None => dest.write_str("none"),
- Scale::Scale(ref x, ref y, ref z) => {
- x.to_css(dest)?;
-
- let is_3d = f32::from(*z) != 1.0;
- if is_3d || x != y {
- dest.write_char(' ')?;
- y.to_css(dest)?;
- }
-
- if is_3d {
- dest.write_char(' ')?;
- z.to_css(dest)?;
- }
- Ok(())
- },
- }
- }
-}
-
-#[inline]
-fn y_axis_and_z_axis_are_zero<LengthPercentage: Zero + ZeroNoPercent, Length: Zero>(
- _: &LengthPercentage,
- y: &LengthPercentage,
- z: &Length,
-) -> bool {
- y.is_zero_no_percent() && z.is_zero()
-}
-
-#[derive(
- Clone,
- Debug,
- Deserialize,
- MallocSizeOf,
- PartialEq,
- Serialize,
- SpecifiedValueInfo,
- ToAnimatedZero,
- ToComputedValue,
- ToCss,
- ToResolvedValue,
- ToShmem,
-)]
-#[repr(C, u8)]
-/// A value of the `translate` property
-///
-/// https://drafts.csswg.org/css-transforms-2/#individual-transform-serialization:
-///
-/// If a 2d translation is specified, the property must serialize with only one
-/// or two values (per usual, if the second value is 0px, the default, it must
-/// be omitted when serializing; however if 0% is the second value, it is included).
-///
-/// If a 3d translation is specified and the value can be expressed as 2d, we treat as 2d and
-/// serialize accoringly. Otherwise, we serialize all three values.
-/// https://github.com/w3c/csswg-drafts/issues/3305
-///
-/// <https://drafts.csswg.org/css-transforms-2/#individual-transforms>
-pub enum GenericTranslate<LengthPercentage, Length>
-where
- LengthPercentage: Zero + ZeroNoPercent,
- Length: Zero,
-{
- /// 'none'
- None,
- /// <length-percentage> [ <length-percentage> <length>? ]?
- Translate(
- LengthPercentage,
- #[css(contextual_skip_if = "y_axis_and_z_axis_are_zero")] LengthPercentage,
- #[css(skip_if = "Zero::is_zero")] Length,
- ),
-}
-
-pub use self::GenericTranslate as Translate;
-
-#[allow(missing_docs)]
-#[derive(
- Clone,
- Copy,
- Debug,
- MallocSizeOf,
- Parse,
- PartialEq,
- SpecifiedValueInfo,
- ToComputedValue,
- ToCss,
- ToResolvedValue,
- ToShmem,
-)]
-#[repr(u8)]
-pub enum TransformStyle {
- Flat,
- #[css(keyword = "preserve-3d")]
- Preserve3d,
-}
diff --git a/components/style/values/generics/ui.rs b/components/style/values/generics/ui.rs
deleted file mode 100644
index 87c8674182c..00000000000
--- a/components/style/values/generics/ui.rs
+++ /dev/null
@@ -1,129 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
-
-//! Generic values for UI properties.
-
-use crate::values::specified::ui::CursorKind;
-use std::fmt::{self, Write};
-use style_traits::{CssWriter, ToCss};
-
-/// A generic value for the `cursor` property.
-///
-/// https://drafts.csswg.org/css-ui/#cursor
-#[derive(
- Clone,
- Debug,
- MallocSizeOf,
- PartialEq,
- SpecifiedValueInfo,
- ToComputedValue,
- ToResolvedValue,
- ToShmem,
-)]
-#[repr(C)]
-pub struct GenericCursor<Image> {
- /// The parsed images for the cursor.
- pub images: crate::OwnedSlice<Image>,
- /// The kind of the cursor [default | help | ...].
- pub keyword: CursorKind,
-}
-
-pub use self::GenericCursor as Cursor;
-
-impl<Image> Cursor<Image> {
- /// Set `cursor` to `auto`
- #[inline]
- pub fn auto() -> Self {
- Self {
- images: Default::default(),
- keyword: CursorKind::Auto,
- }
- }
-}
-
-impl<Image: ToCss> ToCss for Cursor<Image> {
- fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
- where
- W: Write,
- {
- for image in &*self.images {
- image.to_css(dest)?;
- dest.write_str(", ")?;
- }
- self.keyword.to_css(dest)
- }
-}
-
-/// A generic value for item of `image cursors`.
-#[derive(Clone, Debug, MallocSizeOf, PartialEq, ToComputedValue, ToResolvedValue, ToShmem)]
-#[repr(C)]
-pub struct GenericCursorImage<Image, Number> {
- /// The url to parse images from.
- pub image: Image,
- /// Whether the image has a hotspot or not.
- pub has_hotspot: bool,
- /// The x coordinate.
- pub hotspot_x: Number,
- /// The y coordinate.
- pub hotspot_y: Number,
-}
-
-pub use self::GenericCursorImage as CursorImage;
-
-impl<Image: ToCss, Number: ToCss> ToCss for CursorImage<Image, Number> {
- fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
- where
- W: Write,
- {
- self.image.to_css(dest)?;
- if self.has_hotspot {
- dest.write_char(' ')?;
- self.hotspot_x.to_css(dest)?;
- dest.write_char(' ')?;
- self.hotspot_y.to_css(dest)?;
- }
- Ok(())
- }
-}
-
-/// A generic value for `scrollbar-color` property.
-///
-/// https://drafts.csswg.org/css-scrollbars-1/#scrollbar-color
-#[derive(
- Animate,
- Clone,
- ComputeSquaredDistance,
- Copy,
- Debug,
- MallocSizeOf,
- PartialEq,
- SpecifiedValueInfo,
- ToAnimatedValue,
- ToAnimatedZero,
- ToComputedValue,
- ToCss,
- ToResolvedValue,
- ToShmem,
-)]
-#[repr(C, u8)]
-pub enum GenericScrollbarColor<Color> {
- /// `auto`
- Auto,
- /// `<color>{2}`
- Colors {
- /// First `<color>`, for color of the scrollbar thumb.
- thumb: Color,
- /// Second `<color>`, for color of the scrollbar track.
- track: Color,
- },
-}
-
-pub use self::GenericScrollbarColor as ScrollbarColor;
-
-impl<Color> Default for ScrollbarColor<Color> {
- #[inline]
- fn default() -> Self {
- ScrollbarColor::Auto
- }
-}
diff --git a/components/style/values/generics/url.rs b/components/style/values/generics/url.rs
deleted file mode 100644
index 46ed453e82d..00000000000
--- a/components/style/values/generics/url.rs
+++ /dev/null
@@ -1,47 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
-
-//! Generic types for url properties.
-
-/// An image url or none, used for example in list-style-image
-#[derive(
- Animate,
- Clone,
- ComputeSquaredDistance,
- Debug,
- MallocSizeOf,
- PartialEq,
- Parse,
- SpecifiedValueInfo,
- ToAnimatedValue,
- ToAnimatedZero,
- ToComputedValue,
- ToCss,
- ToResolvedValue,
- ToShmem,
-)]
-#[repr(C, u8)]
-pub enum GenericUrlOrNone<U> {
- /// `none`
- None,
- /// A URL.
- Url(U),
-}
-
-pub use self::GenericUrlOrNone as UrlOrNone;
-
-impl<Url> UrlOrNone<Url> {
- /// Initial "none" value for properties such as `list-style-image`
- pub fn none() -> Self {
- UrlOrNone::None
- }
-
- /// Returns whether the value is `none`.
- pub fn is_none(&self) -> bool {
- match *self {
- UrlOrNone::None => true,
- UrlOrNone::Url(..) => false,
- }
- }
-}
diff --git a/components/style/values/mod.rs b/components/style/values/mod.rs
deleted file mode 100644
index 5eb83b1f36b..00000000000
--- a/components/style/values/mod.rs
+++ /dev/null
@@ -1,793 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
-
-//! Common [values][values] used in CSS.
-//!
-//! [values]: https://drafts.csswg.org/css-values/
-
-#![deny(missing_docs)]
-
-use crate::parser::{Parse, ParserContext};
-use crate::values::distance::{ComputeSquaredDistance, SquaredDistance};
-use crate::Atom;
-pub use cssparser::{serialize_identifier, serialize_name, CowRcStr, Parser};
-pub use cssparser::{SourceLocation, Token, RGBA};
-use precomputed_hash::PrecomputedHash;
-use selectors::parser::SelectorParseErrorKind;
-use std::fmt::{self, Debug, Write};
-use style_traits::{CssWriter, ParseError, StyleParseErrorKind, ToCss};
-use to_shmem::impl_trivial_to_shmem;
-
-#[cfg(feature = "gecko")]
-pub use crate::gecko::url::CssUrl;
-#[cfg(feature = "servo")]
-pub use crate::servo::url::CssUrl;
-
-pub mod animated;
-pub mod computed;
-pub mod distance;
-pub mod generics;
-pub mod resolved;
-pub mod specified;
-
-/// A CSS float value.
-pub type CSSFloat = f32;
-
-/// Normalizes a float value to zero after a set of operations that might turn
-/// it into NaN.
-#[inline]
-pub fn normalize(v: CSSFloat) -> CSSFloat {
- if v.is_nan() {
- 0.0
- } else {
- v
- }
-}
-
-/// A CSS integer value.
-pub type CSSInteger = i32;
-
-/// Serialize an identifier which is represented as an atom.
-#[cfg(feature = "gecko")]
-pub fn serialize_atom_identifier<W>(ident: &Atom, dest: &mut W) -> fmt::Result
-where
- W: Write,
-{
- ident.with_str(|s| serialize_identifier(s, dest))
-}
-
-/// Serialize an identifier which is represented as an atom.
-#[cfg(feature = "servo")]
-pub fn serialize_atom_identifier<Static, W>(
- ident: &::string_cache::Atom<Static>,
- dest: &mut W,
-) -> fmt::Result
-where
- Static: string_cache::StaticAtomSet,
- W: Write,
-{
- serialize_identifier(&ident, dest)
-}
-
-/// Serialize a name which is represented as an Atom.
-#[cfg(feature = "gecko")]
-pub fn serialize_atom_name<W>(ident: &Atom, dest: &mut W) -> fmt::Result
-where
- W: Write,
-{
- ident.with_str(|s| serialize_name(s, dest))
-}
-
-/// Serialize a name which is represented as an Atom.
-#[cfg(feature = "servo")]
-pub fn serialize_atom_name<Static, W>(
- ident: &::string_cache::Atom<Static>,
- dest: &mut W,
-) -> fmt::Result
-where
- Static: string_cache::StaticAtomSet,
- W: Write,
-{
- serialize_name(&ident, dest)
-}
-
-fn nan_inf_enabled() -> bool {
- static_prefs::pref!("layout.css.nan-inf.enabled")
-}
-
-/// Serialize a number with calc, and NaN/infinity handling (if enabled)
-pub fn serialize_number<W>(v: f32, was_calc: bool, dest: &mut CssWriter<W>) -> fmt::Result
-where
- W: Write,
-{
- serialize_specified_dimension(v, "", was_calc, dest)
-}
-
-/// Serialize a specified dimension with unit, calc, and NaN/infinity handling (if enabled)
-pub fn serialize_specified_dimension<W>(
- v: f32,
- unit: &str,
- was_calc: bool,
- dest: &mut CssWriter<W>,
-) -> fmt::Result
-where
- W: Write,
-{
- if was_calc {
- dest.write_str("calc(")?;
- }
-
- if !v.is_finite() && nan_inf_enabled() {
- // https://drafts.csswg.org/css-values/#calc-error-constants:
- // "While not technically numbers, these keywords act as numeric values,
- // similar to e and pi. Thus to get an infinite length, for example,
- // requires an expression like calc(infinity * 1px)."
-
- if v.is_nan() {
- dest.write_str("NaN")?;
- } else if v == f32::INFINITY {
- dest.write_str("infinity")?;
- } else if v == f32::NEG_INFINITY {
- dest.write_str("-infinity")?;
- }
-
- if !unit.is_empty() {
- dest.write_str(" * 1")?;
- }
- } else {
- v.to_css(dest)?;
- }
-
- dest.write_str(unit)?;
-
- if was_calc {
- dest.write_char(')')?;
- }
- Ok(())
-}
-
-/// A CSS string stored as an `Atom`.
-#[repr(transparent)]
-#[derive(
- Clone,
- Debug,
- Default,
- Deref,
- Eq,
- Hash,
- MallocSizeOf,
- PartialEq,
- SpecifiedValueInfo,
- ToComputedValue,
- ToResolvedValue,
- ToShmem,
-)]
-pub struct AtomString(pub Atom);
-
-#[cfg(feature = "servo")]
-impl AsRef<str> for AtomString {
- fn as_ref(&self) -> &str {
- &*self.0
- }
-}
-
-impl cssparser::ToCss for AtomString {
- fn to_css<W>(&self, dest: &mut W) -> fmt::Result
- where
- W: Write,
- {
- #[cfg(feature = "servo")]
- {
- cssparser::CssStringWriter::new(dest).write_str(self.as_ref())
- }
- #[cfg(feature = "gecko")]
- {
- self.0
- .with_str(|s| cssparser::CssStringWriter::new(dest).write_str(s))
- }
- }
-}
-
-impl PrecomputedHash for AtomString {
- #[inline]
- fn precomputed_hash(&self) -> u32 {
- self.0.precomputed_hash()
- }
-}
-
-impl<'a> From<&'a str> for AtomString {
- #[inline]
- fn from(string: &str) -> Self {
- Self(Atom::from(string))
- }
-}
-
-/// A generic CSS `<ident>` stored as an `Atom`.
-#[cfg(feature = "servo")]
-#[repr(transparent)]
-#[derive(Deref)]
-pub struct GenericAtomIdent<Set>(pub string_cache::Atom<Set>)
-where
- Set: string_cache::StaticAtomSet;
-
-/// A generic CSS `<ident>` stored as an `Atom`, for the default atom set.
-#[cfg(feature = "servo")]
-pub type AtomIdent = GenericAtomIdent<servo_atoms::AtomStaticSet>;
-
-#[cfg(feature = "servo")]
-impl<Set: string_cache::StaticAtomSet> style_traits::SpecifiedValueInfo for GenericAtomIdent<Set> {}
-
-#[cfg(feature = "servo")]
-impl<Set: string_cache::StaticAtomSet> Default for GenericAtomIdent<Set> {
- fn default() -> Self {
- Self(string_cache::Atom::default())
- }
-}
-
-#[cfg(feature = "servo")]
-impl<Set: string_cache::StaticAtomSet> std::fmt::Debug for GenericAtomIdent<Set> {
- fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
- self.0.fmt(f)
- }
-}
-
-#[cfg(feature = "servo")]
-impl<Set: string_cache::StaticAtomSet> std::hash::Hash for GenericAtomIdent<Set> {
- fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
- self.0.hash(state)
- }
-}
-
-#[cfg(feature = "servo")]
-impl<Set: string_cache::StaticAtomSet> Eq for GenericAtomIdent<Set> {}
-
-#[cfg(feature = "servo")]
-impl<Set: string_cache::StaticAtomSet> PartialEq for GenericAtomIdent<Set> {
- fn eq(&self, other: &Self) -> bool {
- self.0 == other.0
- }
-}
-
-#[cfg(feature = "servo")]
-impl<Set: string_cache::StaticAtomSet> Clone for GenericAtomIdent<Set> {
- fn clone(&self) -> Self {
- Self(self.0.clone())
- }
-}
-
-#[cfg(feature = "servo")]
-impl<Set: string_cache::StaticAtomSet> to_shmem::ToShmem for GenericAtomIdent<Set> {
- fn to_shmem(&self, builder: &mut to_shmem::SharedMemoryBuilder) -> to_shmem::Result<Self> {
- use std::mem::ManuallyDrop;
-
- let atom = self.0.to_shmem(builder)?;
- Ok(ManuallyDrop::new(Self(ManuallyDrop::into_inner(atom))))
- }
-}
-
-#[cfg(feature = "servo")]
-impl<Set: string_cache::StaticAtomSet> malloc_size_of::MallocSizeOf for GenericAtomIdent<Set> {
- fn size_of(&self, ops: &mut malloc_size_of::MallocSizeOfOps) -> usize {
- self.0.size_of(ops)
- }
-}
-
-#[cfg(feature = "servo")]
-impl<Set: string_cache::StaticAtomSet> cssparser::ToCss for GenericAtomIdent<Set> {
- fn to_css<W>(&self, dest: &mut W) -> fmt::Result
- where
- W: Write,
- {
- serialize_atom_identifier(&self.0, dest)
- }
-}
-
-#[cfg(feature = "servo")]
-impl<Set: string_cache::StaticAtomSet> style_traits::ToCss for GenericAtomIdent<Set> {
- fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
- where
- W: Write,
- {
- serialize_atom_identifier(&self.0, dest)
- }
-}
-
-#[cfg(feature = "servo")]
-impl<Set: string_cache::StaticAtomSet> PrecomputedHash for GenericAtomIdent<Set> {
- #[inline]
- fn precomputed_hash(&self) -> u32 {
- self.0.precomputed_hash()
- }
-}
-
-#[cfg(feature = "servo")]
-impl<'a, Set: string_cache::StaticAtomSet> From<&'a str> for GenericAtomIdent<Set> {
- #[inline]
- fn from(string: &str) -> Self {
- Self(string_cache::Atom::from(string))
- }
-}
-
-#[cfg(feature = "servo")]
-impl<Set: string_cache::StaticAtomSet> std::borrow::Borrow<string_cache::Atom<Set>>
- for GenericAtomIdent<Set>
-{
- #[inline]
- fn borrow(&self) -> &string_cache::Atom<Set> {
- &self.0
- }
-}
-
-#[cfg(feature = "servo")]
-impl<Set: string_cache::StaticAtomSet> GenericAtomIdent<Set> {
- /// Constructs a new GenericAtomIdent.
- #[inline]
- pub fn new(atom: string_cache::Atom<Set>) -> Self {
- Self(atom)
- }
-
- /// Cast an atom ref to an AtomIdent ref.
- #[inline]
- pub fn cast<'a>(atom: &'a string_cache::Atom<Set>) -> &'a Self {
- let ptr = atom as *const _ as *const Self;
- // safety: repr(transparent)
- unsafe { &*ptr }
- }
-}
-
-/// A CSS `<ident>` stored as an `Atom`.
-#[cfg(feature = "gecko")]
-#[repr(transparent)]
-#[derive(
- Clone, Debug, Default, Deref, Eq, Hash, MallocSizeOf, PartialEq, SpecifiedValueInfo, ToShmem,
-)]
-pub struct AtomIdent(pub Atom);
-
-#[cfg(feature = "gecko")]
-impl cssparser::ToCss for AtomIdent {
- fn to_css<W>(&self, dest: &mut W) -> fmt::Result
- where
- W: Write,
- {
- serialize_atom_identifier(&self.0, dest)
- }
-}
-
-#[cfg(feature = "gecko")]
-impl style_traits::ToCss for AtomIdent {
- fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
- where
- W: Write,
- {
- cssparser::ToCss::to_css(self, dest)
- }
-}
-
-#[cfg(feature = "gecko")]
-impl PrecomputedHash for AtomIdent {
- #[inline]
- fn precomputed_hash(&self) -> u32 {
- self.0.precomputed_hash()
- }
-}
-
-#[cfg(feature = "gecko")]
-impl<'a> From<&'a str> for AtomIdent {
- #[inline]
- fn from(string: &str) -> Self {
- Self(Atom::from(string))
- }
-}
-
-#[cfg(feature = "gecko")]
-impl AtomIdent {
- /// Constructs a new AtomIdent.
- #[inline]
- pub fn new(atom: Atom) -> Self {
- Self(atom)
- }
-
- /// Like `Atom::with` but for `AtomIdent`.
- pub unsafe fn with<F, R>(ptr: *const crate::gecko_bindings::structs::nsAtom, callback: F) -> R
- where
- F: FnOnce(&Self) -> R,
- {
- Atom::with(ptr, |atom: &Atom| {
- // safety: repr(transparent)
- let atom = atom as *const Atom as *const AtomIdent;
- callback(&*atom)
- })
- }
-
- /// Cast an atom ref to an AtomIdent ref.
- #[inline]
- pub fn cast<'a>(atom: &'a Atom) -> &'a Self {
- let ptr = atom as *const _ as *const Self;
- // safety: repr(transparent)
- unsafe { &*ptr }
- }
-}
-
-#[cfg(feature = "gecko")]
-impl std::borrow::Borrow<crate::gecko_string_cache::WeakAtom> for AtomIdent {
- #[inline]
- fn borrow(&self) -> &crate::gecko_string_cache::WeakAtom {
- self.0.borrow()
- }
-}
-
-/// Serialize a value into percentage.
-pub fn serialize_percentage<W>(value: CSSFloat, dest: &mut CssWriter<W>) -> fmt::Result
-where
- W: Write,
-{
- serialize_specified_dimension(value * 100., "%", /* was_calc = */ false, dest)
-}
-
-/// Serialize a value into normalized (no NaN/inf serialization) percentage.
-pub fn serialize_normalized_percentage<W>(value: CSSFloat, dest: &mut CssWriter<W>) -> fmt::Result
-where
- W: Write,
-{
- (value * 100.).to_css(dest)?;
- dest.write_char('%')
-}
-
-/// Convenience void type to disable some properties and values through types.
-#[cfg_attr(feature = "servo", derive(Deserialize, MallocSizeOf, Serialize))]
-#[derive(
- Clone,
- Copy,
- Debug,
- PartialEq,
- SpecifiedValueInfo,
- ToAnimatedValue,
- ToComputedValue,
- ToCss,
- ToResolvedValue,
-)]
-pub enum Impossible {}
-
-// FIXME(nox): This should be derived but the derive code cannot cope
-// with uninhabited enums.
-impl ComputeSquaredDistance for Impossible {
- #[inline]
- fn compute_squared_distance(&self, _other: &Self) -> Result<SquaredDistance, ()> {
- match *self {}
- }
-}
-
-impl_trivial_to_shmem!(Impossible);
-
-impl Parse for Impossible {
- fn parse<'i, 't>(
- _context: &ParserContext,
- input: &mut Parser<'i, 't>,
- ) -> Result<Self, ParseError<'i>> {
- Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError))
- }
-}
-
-/// A struct representing one of two kinds of values.
-#[derive(
- Animate,
- Clone,
- ComputeSquaredDistance,
- Copy,
- MallocSizeOf,
- PartialEq,
- Parse,
- SpecifiedValueInfo,
- ToAnimatedValue,
- ToAnimatedZero,
- ToComputedValue,
- ToCss,
- ToResolvedValue,
- ToShmem,
-)]
-pub enum Either<A, B> {
- /// The first value.
- First(A),
- /// The second kind of value.
- Second(B),
-}
-
-impl<A: Debug, B: Debug> Debug for Either<A, B> {
- fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
- match *self {
- Either::First(ref v) => v.fmt(f),
- Either::Second(ref v) => v.fmt(f),
- }
- }
-}
-
-/// <https://drafts.csswg.org/css-values-4/#custom-idents>
-#[derive(
- Clone,
- Debug,
- Eq,
- Hash,
- MallocSizeOf,
- PartialEq,
- SpecifiedValueInfo,
- ToComputedValue,
- ToResolvedValue,
- ToShmem,
-)]
-#[repr(C)]
-pub struct CustomIdent(pub Atom);
-
-impl CustomIdent {
- /// Parse an already-tokenizer identifier
- pub fn from_ident<'i>(
- location: SourceLocation,
- ident: &CowRcStr<'i>,
- excluding: &[&str],
- ) -> Result<Self, ParseError<'i>> {
- if !Self::is_valid(ident, excluding) {
- return Err(
- location.new_custom_error(SelectorParseErrorKind::UnexpectedIdent(ident.clone()))
- );
- }
- if excluding.iter().any(|s| ident.eq_ignore_ascii_case(s)) {
- Err(location.new_custom_error(StyleParseErrorKind::UnspecifiedError))
- } else {
- Ok(CustomIdent(Atom::from(ident.as_ref())))
- }
- }
-
- fn is_valid(ident: &str, excluding: &[&str]) -> bool {
- use crate::properties::CSSWideKeyword;
- // https://drafts.csswg.org/css-values-4/#custom-idents:
- //
- // The CSS-wide keywords are not valid <custom-ident>s. The default
- // keyword is reserved and is also not a valid <custom-ident>.
- if CSSWideKeyword::from_ident(ident).is_ok() || ident.eq_ignore_ascii_case("default") {
- return false;
- }
-
- // https://drafts.csswg.org/css-values-4/#custom-idents:
- //
- // Excluded keywords are excluded in all ASCII case permutations.
- !excluding.iter().any(|s| ident.eq_ignore_ascii_case(s))
- }
-}
-
-impl ToCss for CustomIdent {
- fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
- where
- W: Write,
- {
- serialize_atom_identifier(&self.0, dest)
- }
-}
-
-/// <https://www.w3.org/TR/css-values-4/#dashed-idents>
-/// This is simply an Atom, but will only parse if the identifier starts with "--".
-#[repr(transparent)]
-#[derive(
- Clone,
- Debug,
- Eq,
- Hash,
- MallocSizeOf,
- PartialEq,
- SpecifiedValueInfo,
- ToComputedValue,
- ToResolvedValue,
- ToShmem,
-)]
-pub struct DashedIdent(pub Atom);
-
-impl Parse for DashedIdent {
- fn parse<'i, 't>(
- _: &ParserContext,
- input: &mut Parser<'i, 't>,
- ) -> Result<Self, ParseError<'i>> {
- let location = input.current_source_location();
- let ident = input.expect_ident()?;
- if ident.starts_with("--") {
- Ok(Self(Atom::from(ident.as_ref())))
- } else {
- Err(location.new_custom_error(SelectorParseErrorKind::UnexpectedIdent(ident.clone())))
- }
- }
-}
-
-impl ToCss for DashedIdent {
- fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
- where
- W: Write,
- {
- serialize_atom_identifier(&self.0, dest)
- }
-}
-
-/// The <timeline-name> or <keyframes-name>.
-/// The definition of these two names are the same, so we use the same type for them.
-///
-/// <https://drafts.csswg.org/css-animations-2/#typedef-timeline-name>
-/// <https://drafts.csswg.org/css-animations/#typedef-keyframes-name>
-///
-/// We use a single atom for these. Empty atom represents `none` animation.
-#[repr(transparent)]
-#[derive(
- Clone,
- Debug,
- Hash,
- PartialEq,
- MallocSizeOf,
- SpecifiedValueInfo,
- ToComputedValue,
- ToResolvedValue,
- ToShmem,
-)]
-pub struct TimelineOrKeyframesName(Atom);
-
-impl TimelineOrKeyframesName {
- /// <https://drafts.csswg.org/css-animations/#dom-csskeyframesrule-name>
- pub fn from_ident(value: &str) -> Self {
- Self(Atom::from(value))
- }
-
- /// Returns the `none` value.
- pub fn none() -> Self {
- Self(atom!(""))
- }
-
- /// Returns whether this is the special `none` value.
- pub fn is_none(&self) -> bool {
- self.0 == atom!("")
- }
-
- /// Create a new TimelineOrKeyframesName from Atom.
- #[cfg(feature = "gecko")]
- pub fn from_atom(atom: Atom) -> Self {
- Self(atom)
- }
-
- /// The name as an Atom
- pub fn as_atom(&self) -> &Atom {
- &self.0
- }
-
- fn parse<'i, 't>(input: &mut Parser<'i, 't>, invalid: &[&str]) -> Result<Self, ParseError<'i>> {
- debug_assert!(invalid.contains(&"none"));
- let location = input.current_source_location();
- Ok(match *input.next()? {
- Token::Ident(ref s) => Self(CustomIdent::from_ident(location, s, invalid)?.0),
- Token::QuotedString(ref s) => Self(Atom::from(s.as_ref())),
- ref t => return Err(location.new_unexpected_token_error(t.clone())),
- })
- }
-
- fn to_css<W>(&self, dest: &mut CssWriter<W>, invalid: &[&str]) -> fmt::Result
- where
- W: Write,
- {
- debug_assert!(invalid.contains(&"none"));
-
- if self.0 == atom!("") {
- return dest.write_str("none");
- }
-
- let mut serialize = |s: &_| {
- if CustomIdent::is_valid(s, invalid) {
- serialize_identifier(s, dest)
- } else {
- s.to_css(dest)
- }
- };
- #[cfg(feature = "gecko")]
- return self.0.with_str(|s| serialize(s));
- #[cfg(feature = "servo")]
- return serialize(self.0.as_ref());
- }
-}
-
-impl Eq for TimelineOrKeyframesName {}
-
-/// The typedef of <timeline-name>.
-#[repr(transparent)]
-#[derive(
- Clone,
- Debug,
- Deref,
- Hash,
- Eq,
- PartialEq,
- MallocSizeOf,
- SpecifiedValueInfo,
- ToComputedValue,
- ToResolvedValue,
- ToShmem,
-)]
-pub struct TimelineName(TimelineOrKeyframesName);
-
-impl TimelineName {
- /// Create a new TimelineName from Atom.
- #[cfg(feature = "gecko")]
- pub fn from_atom(atom: Atom) -> Self {
- Self(TimelineOrKeyframesName::from_atom(atom))
- }
-
- /// Returns the `none` value.
- pub fn none() -> Self {
- Self(TimelineOrKeyframesName::none())
- }
-}
-
-impl Parse for TimelineName {
- fn parse<'i, 't>(
- _: &ParserContext,
- input: &mut Parser<'i, 't>,
- ) -> Result<Self, ParseError<'i>> {
- Ok(Self(TimelineOrKeyframesName::parse(
- input,
- &["none", "auto"],
- )?))
- }
-}
-
-impl ToCss for TimelineName {
- fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
- where
- W: Write,
- {
- self.0.to_css(dest, &["none", "auto"])
- }
-}
-
-/// The typedef of <keyframes-name>.
-#[repr(transparent)]
-#[derive(
- Clone,
- Debug,
- Deref,
- Hash,
- Eq,
- PartialEq,
- MallocSizeOf,
- SpecifiedValueInfo,
- ToComputedValue,
- ToResolvedValue,
- ToShmem,
-)]
-pub struct KeyframesName(TimelineOrKeyframesName);
-
-impl KeyframesName {
- /// Create a new KeyframesName from Atom.
- #[cfg(feature = "gecko")]
- pub fn from_atom(atom: Atom) -> Self {
- Self(TimelineOrKeyframesName::from_atom(atom))
- }
-
- /// <https://drafts.csswg.org/css-animations/#dom-csskeyframesrule-name>
- pub fn from_ident(value: &str) -> Self {
- Self(TimelineOrKeyframesName::from_ident(value))
- }
-
- /// Returns the `none` value.
- pub fn none() -> Self {
- Self(TimelineOrKeyframesName::none())
- }
-}
-
-impl Parse for KeyframesName {
- fn parse<'i, 't>(
- _: &ParserContext,
- input: &mut Parser<'i, 't>,
- ) -> Result<Self, ParseError<'i>> {
- Ok(Self(TimelineOrKeyframesName::parse(input, &["none"])?))
- }
-}
-
-impl ToCss for KeyframesName {
- fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
- where
- W: Write,
- {
- self.0.to_css(dest, &["none"])
- }
-}
diff --git a/components/style/values/resolved/color.rs b/components/style/values/resolved/color.rs
deleted file mode 100644
index 79dfd8685fe..00000000000
--- a/components/style/values/resolved/color.rs
+++ /dev/null
@@ -1,48 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
-
-//! Resolved color values.
-
-use super::{Context, ToResolvedValue};
-
-use crate::color::AbsoluteColor;
-use crate::values::computed::color as computed;
-use crate::values::generics::color as generics;
-
-impl ToResolvedValue for computed::Color {
- // A resolved color value is an rgba color, with currentcolor resolved.
- type ResolvedValue = AbsoluteColor;
-
- #[inline]
- fn to_resolved_value(self, context: &Context) -> Self::ResolvedValue {
- context.style.resolve_color(self)
- }
-
- #[inline]
- fn from_resolved_value(resolved: Self::ResolvedValue) -> Self {
- generics::Color::Absolute(resolved)
- }
-}
-
-impl ToResolvedValue for computed::CaretColor {
- // A resolved caret-color value is an rgba color, with auto resolving to
- // currentcolor.
- type ResolvedValue = AbsoluteColor;
-
- #[inline]
- fn to_resolved_value(self, context: &Context) -> Self::ResolvedValue {
- let color = match self.0 {
- generics::ColorOrAuto::Color(color) => color,
- generics::ColorOrAuto::Auto => generics::Color::currentcolor(),
- };
- color.to_resolved_value(context)
- }
-
- #[inline]
- fn from_resolved_value(resolved: Self::ResolvedValue) -> Self {
- generics::CaretColor(generics::ColorOrAuto::Color(
- computed::Color::from_resolved_value(resolved),
- ))
- }
-}
diff --git a/components/style/values/resolved/counters.rs b/components/style/values/resolved/counters.rs
deleted file mode 100644
index c1332449ad1..00000000000
--- a/components/style/values/resolved/counters.rs
+++ /dev/null
@@ -1,51 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
-
-//! Resolved values for counter properties
-
-use super::{Context, ToResolvedValue};
-use crate::values::computed;
-
-/// https://drafts.csswg.org/css-content/#content-property
-///
-/// We implement this at resolved value time because otherwise it causes us to
-/// allocate a bunch of useless initial structs for ::before / ::after, which is
-/// a bit unfortunate.
-///
-/// Though these should be temporary, mostly, so if this causes complexity in
-/// other places, it should be fine to move to `StyleAdjuster`.
-///
-/// See https://github.com/w3c/csswg-drafts/issues/4632 for where some related
-/// issues are being discussed.
-impl ToResolvedValue for computed::Content {
- type ResolvedValue = Self;
-
- #[inline]
- fn to_resolved_value(self, context: &Context) -> Self {
- let (is_pseudo, is_before_or_after, is_marker) = match context.style.pseudo() {
- Some(ref pseudo) => (true, pseudo.is_before_or_after(), pseudo.is_marker()),
- None => (false, false, false),
- };
- match self {
- Self::Normal if is_before_or_after => Self::None,
- // For now, make `content: none` compute to `normal` for pseudos
- // other than ::before, ::after and ::marker, as we don't respect it.
- // https://github.com/w3c/csswg-drafts/issues/6124
- // Ditto for non-pseudo elements if the pref is disabled.
- Self::None
- if (is_pseudo && !is_before_or_after && !is_marker) ||
- (!is_pseudo &&
- !static_prefs::pref!("layout.css.element-content-none.enabled")) =>
- {
- Self::Normal
- },
- other => other,
- }
- }
-
- #[inline]
- fn from_resolved_value(resolved: Self) -> Self {
- resolved
- }
-}
diff --git a/components/style/values/resolved/mod.rs b/components/style/values/resolved/mod.rs
deleted file mode 100644
index 7f15ee452f5..00000000000
--- a/components/style/values/resolved/mod.rs
+++ /dev/null
@@ -1,277 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
-
-//! Resolved values. These are almost always computed values, but in some cases
-//! there are used values.
-
-#[cfg(feature = "gecko")]
-use crate::media_queries::Device;
-use crate::properties::ComputedValues;
-use crate::ArcSlice;
-use servo_arc::Arc;
-use smallvec::SmallVec;
-
-mod color;
-mod counters;
-
-use crate::values::computed;
-
-/// Element-specific information needed to resolve property values.
-#[cfg(feature = "gecko")]
-pub struct ResolvedElementInfo<'a> {
- /// Element we're resolving line-height against.
- pub element: crate::gecko::wrapper::GeckoElement<'a>,
-}
-
-/// Information needed to resolve a given value.
-pub struct Context<'a> {
- /// The style we're resolving for. This is useful to resolve currentColor.
- pub style: &'a ComputedValues,
- /// The device / document we're resolving style for. Useful to do font metrics stuff needed for
- /// line-height.
- #[cfg(feature = "gecko")]
- pub device: &'a Device,
- /// The element-specific information to resolve the value.
- #[cfg(feature = "gecko")]
- pub element_info: ResolvedElementInfo<'a>,
-}
-
-/// A trait to represent the conversion between resolved and resolved values.
-///
-/// This trait is derivable with `#[derive(ToResolvedValue)]`.
-///
-/// The deriving code assumes that if the type isn't generic, then the trait can
-/// be implemented as simple move. This means that a manual implementation with
-/// `ResolvedValue = Self` is bogus if it returns anything else than a clone.
-pub trait ToResolvedValue {
- /// The resolved value type we're going to be converted to.
- type ResolvedValue;
-
- /// Convert a resolved value to a resolved value.
- fn to_resolved_value(self, context: &Context) -> Self::ResolvedValue;
-
- /// Convert a resolved value to resolved value form.
- fn from_resolved_value(resolved: Self::ResolvedValue) -> Self;
-}
-
-macro_rules! trivial_to_resolved_value {
- ($ty:ty) => {
- impl $crate::values::resolved::ToResolvedValue for $ty {
- type ResolvedValue = Self;
-
- #[inline]
- fn to_resolved_value(self, _: &Context) -> Self {
- self
- }
-
- #[inline]
- fn from_resolved_value(resolved: Self::ResolvedValue) -> Self {
- resolved
- }
- }
- };
-}
-
-trivial_to_resolved_value!(());
-trivial_to_resolved_value!(bool);
-trivial_to_resolved_value!(f32);
-trivial_to_resolved_value!(u8);
-trivial_to_resolved_value!(i8);
-trivial_to_resolved_value!(u16);
-trivial_to_resolved_value!(i16);
-trivial_to_resolved_value!(u32);
-trivial_to_resolved_value!(i32);
-trivial_to_resolved_value!(usize);
-trivial_to_resolved_value!(String);
-trivial_to_resolved_value!(Box<str>);
-trivial_to_resolved_value!(crate::OwnedStr);
-trivial_to_resolved_value!(crate::color::AbsoluteColor);
-trivial_to_resolved_value!(crate::Atom);
-trivial_to_resolved_value!(crate::values::AtomIdent);
-trivial_to_resolved_value!(app_units::Au);
-trivial_to_resolved_value!(computed::url::ComputedUrl);
-#[cfg(feature = "gecko")]
-trivial_to_resolved_value!(computed::url::ComputedImageUrl);
-#[cfg(feature = "servo")]
-trivial_to_resolved_value!(crate::Namespace);
-#[cfg(feature = "servo")]
-trivial_to_resolved_value!(crate::Prefix);
-trivial_to_resolved_value!(computed::LengthPercentage);
-trivial_to_resolved_value!(style_traits::values::specified::AllowedNumericType);
-trivial_to_resolved_value!(computed::TimingFunction);
-
-impl<A, B> ToResolvedValue for (A, B)
-where
- A: ToResolvedValue,
- B: ToResolvedValue,
-{
- type ResolvedValue = (
- <A as ToResolvedValue>::ResolvedValue,
- <B as ToResolvedValue>::ResolvedValue,
- );
-
- #[inline]
- fn to_resolved_value(self, context: &Context) -> Self::ResolvedValue {
- (
- self.0.to_resolved_value(context),
- self.1.to_resolved_value(context),
- )
- }
-
- #[inline]
- fn from_resolved_value(resolved: Self::ResolvedValue) -> Self {
- (
- A::from_resolved_value(resolved.0),
- B::from_resolved_value(resolved.1),
- )
- }
-}
-
-impl<T> ToResolvedValue for Option<T>
-where
- T: ToResolvedValue,
-{
- type ResolvedValue = Option<<T as ToResolvedValue>::ResolvedValue>;
-
- #[inline]
- fn to_resolved_value(self, context: &Context) -> Self::ResolvedValue {
- self.map(|item| item.to_resolved_value(context))
- }
-
- #[inline]
- fn from_resolved_value(resolved: Self::ResolvedValue) -> Self {
- resolved.map(T::from_resolved_value)
- }
-}
-
-impl<T> ToResolvedValue for SmallVec<[T; 1]>
-where
- T: ToResolvedValue,
-{
- type ResolvedValue = SmallVec<[<T as ToResolvedValue>::ResolvedValue; 1]>;
-
- #[inline]
- fn to_resolved_value(self, context: &Context) -> Self::ResolvedValue {
- self.into_iter()
- .map(|item| item.to_resolved_value(context))
- .collect()
- }
-
- #[inline]
- fn from_resolved_value(resolved: Self::ResolvedValue) -> Self {
- resolved.into_iter().map(T::from_resolved_value).collect()
- }
-}
-
-impl<T> ToResolvedValue for Vec<T>
-where
- T: ToResolvedValue,
-{
- type ResolvedValue = Vec<<T as ToResolvedValue>::ResolvedValue>;
-
- #[inline]
- fn to_resolved_value(self, context: &Context) -> Self::ResolvedValue {
- self.into_iter()
- .map(|item| item.to_resolved_value(context))
- .collect()
- }
-
- #[inline]
- fn from_resolved_value(resolved: Self::ResolvedValue) -> Self {
- resolved.into_iter().map(T::from_resolved_value).collect()
- }
-}
-
-impl<T> ToResolvedValue for Box<T>
-where
- T: ToResolvedValue,
-{
- type ResolvedValue = Box<<T as ToResolvedValue>::ResolvedValue>;
-
- #[inline]
- fn to_resolved_value(self, context: &Context) -> Self::ResolvedValue {
- Box::new(T::to_resolved_value(*self, context))
- }
-
- #[inline]
- fn from_resolved_value(resolved: Self::ResolvedValue) -> Self {
- Box::new(T::from_resolved_value(*resolved))
- }
-}
-
-impl<T> ToResolvedValue for Box<[T]>
-where
- T: ToResolvedValue,
-{
- type ResolvedValue = Box<[<T as ToResolvedValue>::ResolvedValue]>;
-
- #[inline]
- fn to_resolved_value(self, context: &Context) -> Self::ResolvedValue {
- Vec::from(self)
- .to_resolved_value(context)
- .into_boxed_slice()
- }
-
- #[inline]
- fn from_resolved_value(resolved: Self::ResolvedValue) -> Self {
- Vec::from_resolved_value(Vec::from(resolved)).into_boxed_slice()
- }
-}
-
-impl<T> ToResolvedValue for crate::OwnedSlice<T>
-where
- T: ToResolvedValue,
-{
- type ResolvedValue = crate::OwnedSlice<<T as ToResolvedValue>::ResolvedValue>;
-
- #[inline]
- fn to_resolved_value(self, context: &Context) -> Self::ResolvedValue {
- self.into_box().to_resolved_value(context).into()
- }
-
- #[inline]
- fn from_resolved_value(resolved: Self::ResolvedValue) -> Self {
- Self::from(Box::from_resolved_value(resolved.into_box()))
- }
-}
-
-// NOTE(emilio): This is implementable more generically, but it's unlikely what
-// you want there, as it forces you to have an extra allocation.
-//
-// We could do that if needed, ideally with specialization for the case where
-// ResolvedValue = T. But we don't need it for now.
-impl<T> ToResolvedValue for Arc<T>
-where
- T: ToResolvedValue<ResolvedValue = T>,
-{
- type ResolvedValue = Self;
-
- #[inline]
- fn to_resolved_value(self, _: &Context) -> Self {
- self
- }
-
- #[inline]
- fn from_resolved_value(resolved: Self) -> Self {
- resolved
- }
-}
-
-// Same caveat as above applies.
-impl<T> ToResolvedValue for ArcSlice<T>
-where
- T: ToResolvedValue<ResolvedValue = T>,
-{
- type ResolvedValue = Self;
-
- #[inline]
- fn to_resolved_value(self, _: &Context) -> Self {
- self
- }
-
- #[inline]
- fn from_resolved_value(resolved: Self) -> Self {
- resolved
- }
-}
diff --git a/components/style/values/specified/align.rs b/components/style/values/specified/align.rs
deleted file mode 100644
index ebb6f638340..00000000000
--- a/components/style/values/specified/align.rs
+++ /dev/null
@@ -1,817 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
-
-//! Values for CSS Box Alignment properties
-//!
-//! https://drafts.csswg.org/css-align/
-
-use crate::parser::{Parse, ParserContext};
-use cssparser::Parser;
-use std::fmt::{self, Write};
-use style_traits::{CssWriter, KeywordsCollectFn, ParseError, SpecifiedValueInfo, ToCss};
-
-bitflags! {
- /// Constants shared by multiple CSS Box Alignment properties
- #[derive(MallocSizeOf, ToComputedValue, ToResolvedValue, ToShmem)]
- #[repr(C)]
- pub struct AlignFlags: u8 {
- // Enumeration stored in the lower 5 bits:
- /// {align,justify}-{content,items,self}: 'auto'
- const AUTO = 0;
- /// 'normal'
- const NORMAL = 1;
- /// 'start'
- const START = 2;
- /// 'end'
- const END = 3;
- /// 'flex-start'
- const FLEX_START = 4;
- /// 'flex-end'
- const FLEX_END = 5;
- /// 'center'
- const CENTER = 6;
- /// 'left'
- const LEFT = 7;
- /// 'right'
- const RIGHT = 8;
- /// 'baseline'
- const BASELINE = 9;
- /// 'last-baseline'
- const LAST_BASELINE = 10;
- /// 'stretch'
- const STRETCH = 11;
- /// 'self-start'
- const SELF_START = 12;
- /// 'self-end'
- const SELF_END = 13;
- /// 'space-between'
- const SPACE_BETWEEN = 14;
- /// 'space-around'
- const SPACE_AROUND = 15;
- /// 'space-evenly'
- const SPACE_EVENLY = 16;
-
- // Additional flags stored in the upper bits:
- /// 'legacy' (mutually exclusive w. SAFE & UNSAFE)
- const LEGACY = 1 << 5;
- /// 'safe'
- const SAFE = 1 << 6;
- /// 'unsafe' (mutually exclusive w. SAFE)
- const UNSAFE = 1 << 7;
-
- /// Mask for the additional flags above.
- const FLAG_BITS = 0b11100000;
- }
-}
-
-impl AlignFlags {
- /// Returns the enumeration value stored in the lower 5 bits.
- #[inline]
- fn value(&self) -> Self {
- *self & !AlignFlags::FLAG_BITS
- }
-}
-
-impl ToCss for AlignFlags {
- fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
- where
- W: Write,
- {
- let extra_flags = *self & AlignFlags::FLAG_BITS;
- let value = self.value();
-
- match extra_flags {
- AlignFlags::LEGACY => {
- dest.write_str("legacy")?;
- if value.is_empty() {
- return Ok(());
- }
- dest.write_char(' ')?;
- },
- AlignFlags::SAFE => dest.write_str("safe ")?,
- AlignFlags::UNSAFE => dest.write_str("unsafe ")?,
- _ => {
- debug_assert_eq!(extra_flags, AlignFlags::empty());
- },
- }
-
- dest.write_str(match value {
- AlignFlags::AUTO => "auto",
- AlignFlags::NORMAL => "normal",
- AlignFlags::START => "start",
- AlignFlags::END => "end",
- AlignFlags::FLEX_START => "flex-start",
- AlignFlags::FLEX_END => "flex-end",
- AlignFlags::CENTER => "center",
- AlignFlags::LEFT => "left",
- AlignFlags::RIGHT => "right",
- AlignFlags::BASELINE => "baseline",
- AlignFlags::LAST_BASELINE => "last baseline",
- AlignFlags::STRETCH => "stretch",
- AlignFlags::SELF_START => "self-start",
- AlignFlags::SELF_END => "self-end",
- AlignFlags::SPACE_BETWEEN => "space-between",
- AlignFlags::SPACE_AROUND => "space-around",
- AlignFlags::SPACE_EVENLY => "space-evenly",
- _ => unreachable!(),
- })
- }
-}
-
-/// An axis direction, either inline (for the `justify` properties) or block,
-/// (for the `align` properties).
-#[derive(Clone, Copy, PartialEq)]
-pub enum AxisDirection {
- /// Block direction.
- Block,
- /// Inline direction.
- Inline,
-}
-
-/// Shared value for the `align-content` and `justify-content` properties.
-///
-/// <https://drafts.csswg.org/css-align/#content-distribution>
-#[derive(
- Clone,
- Copy,
- Debug,
- Eq,
- MallocSizeOf,
- PartialEq,
- ToComputedValue,
- ToCss,
- ToResolvedValue,
- ToShmem,
-)]
-#[repr(C)]
-#[cfg_attr(feature = "servo", derive(Deserialize, Serialize))]
-pub struct ContentDistribution {
- primary: AlignFlags,
- // FIXME(https://github.com/w3c/csswg-drafts/issues/1002): This will need to
- // accept fallback alignment, eventually.
-}
-
-impl ContentDistribution {
- /// The initial value 'normal'
- #[inline]
- pub fn normal() -> Self {
- Self::new(AlignFlags::NORMAL)
- }
-
- /// `start`
- #[inline]
- pub fn start() -> Self {
- Self::new(AlignFlags::START)
- }
-
- /// The initial value 'normal'
- #[inline]
- pub fn new(primary: AlignFlags) -> Self {
- Self { primary }
- }
-
- /// Returns whether this value is a <baseline-position>.
- pub fn is_baseline_position(&self) -> bool {
- matches!(
- self.primary.value(),
- AlignFlags::BASELINE | AlignFlags::LAST_BASELINE
- )
- }
-
- /// The primary alignment
- #[inline]
- pub fn primary(self) -> AlignFlags {
- self.primary
- }
-
- /// Parse a value for align-content / justify-content.
- pub fn parse<'i, 't>(
- input: &mut Parser<'i, 't>,
- axis: AxisDirection,
- ) -> Result<Self, ParseError<'i>> {
- // NOTE Please also update the `list_keywords` function below
- // when this function is updated.
-
- // Try to parse normal first
- if input
- .try_parse(|i| i.expect_ident_matching("normal"))
- .is_ok()
- {
- return Ok(ContentDistribution::normal());
- }
-
- // Parse <baseline-position>, but only on the block axis.
- if axis == AxisDirection::Block {
- if let Ok(value) = input.try_parse(parse_baseline) {
- return Ok(ContentDistribution::new(value));
- }
- }
-
- // <content-distribution>
- if let Ok(value) = input.try_parse(parse_content_distribution) {
- return Ok(ContentDistribution::new(value));
- }
-
- // <overflow-position>? <content-position>
- let overflow_position = input
- .try_parse(parse_overflow_position)
- .unwrap_or(AlignFlags::empty());
-
- let content_position = try_match_ident_ignore_ascii_case! { input,
- "start" => AlignFlags::START,
- "end" => AlignFlags::END,
- "flex-start" => AlignFlags::FLEX_START,
- "flex-end" => AlignFlags::FLEX_END,
- "center" => AlignFlags::CENTER,
- "left" if axis == AxisDirection::Inline => AlignFlags::LEFT,
- "right" if axis == AxisDirection::Inline => AlignFlags::RIGHT,
- };
-
- Ok(ContentDistribution::new(
- content_position | overflow_position,
- ))
- }
-
- fn list_keywords(f: KeywordsCollectFn, axis: AxisDirection) {
- f(&["normal"]);
- if axis == AxisDirection::Block {
- list_baseline_keywords(f);
- }
- list_content_distribution_keywords(f);
- list_overflow_position_keywords(f);
- f(&["start", "end", "flex-start", "flex-end", "center"]);
- if axis == AxisDirection::Inline {
- f(&["left", "right"]);
- }
- }
-}
-
-/// Value for the `align-content` property.
-///
-/// <https://drafts.csswg.org/css-align/#propdef-align-content>
-#[derive(
- Clone,
- Copy,
- Debug,
- Eq,
- MallocSizeOf,
- PartialEq,
- ToComputedValue,
- ToCss,
- ToResolvedValue,
- ToShmem,
-)]
-#[repr(transparent)]
-pub struct AlignContent(pub ContentDistribution);
-
-impl Parse for AlignContent {
- fn parse<'i, 't>(
- _: &ParserContext,
- input: &mut Parser<'i, 't>,
- ) -> Result<Self, ParseError<'i>> {
- // NOTE Please also update `impl SpecifiedValueInfo` below when
- // this function is updated.
- Ok(AlignContent(ContentDistribution::parse(
- input,
- AxisDirection::Block,
- )?))
- }
-}
-
-impl SpecifiedValueInfo for AlignContent {
- fn collect_completion_keywords(f: KeywordsCollectFn) {
- ContentDistribution::list_keywords(f, AxisDirection::Block);
- }
-}
-
-/// Value for the `align-tracks` property.
-///
-/// <https://github.com/w3c/csswg-drafts/issues/4650>
-#[derive(
- Clone,
- Debug,
- Default,
- Eq,
- MallocSizeOf,
- PartialEq,
- SpecifiedValueInfo,
- ToComputedValue,
- ToCss,
- ToResolvedValue,
- ToShmem,
-)]
-#[repr(transparent)]
-#[css(comma)]
-pub struct AlignTracks(#[css(iterable, if_empty = "normal")] pub crate::OwnedSlice<AlignContent>);
-
-impl Parse for AlignTracks {
- fn parse<'i, 't>(
- context: &ParserContext,
- input: &mut Parser<'i, 't>,
- ) -> Result<Self, ParseError<'i>> {
- let values = input.parse_comma_separated(|input| AlignContent::parse(context, input))?;
- Ok(AlignTracks(values.into()))
- }
-}
-
-/// Value for the `justify-content` property.
-///
-/// <https://drafts.csswg.org/css-align/#propdef-justify-content>
-#[derive(
- Clone,
- Copy,
- Debug,
- Eq,
- MallocSizeOf,
- PartialEq,
- ToComputedValue,
- ToCss,
- ToResolvedValue,
- ToShmem,
-)]
-#[repr(transparent)]
-pub struct JustifyContent(pub ContentDistribution);
-
-impl Parse for JustifyContent {
- fn parse<'i, 't>(
- _: &ParserContext,
- input: &mut Parser<'i, 't>,
- ) -> Result<Self, ParseError<'i>> {
- // NOTE Please also update `impl SpecifiedValueInfo` below when
- // this function is updated.
- Ok(JustifyContent(ContentDistribution::parse(
- input,
- AxisDirection::Inline,
- )?))
- }
-}
-
-impl SpecifiedValueInfo for JustifyContent {
- fn collect_completion_keywords(f: KeywordsCollectFn) {
- ContentDistribution::list_keywords(f, AxisDirection::Inline);
- }
-}
-/// Value for the `justify-tracks` property.
-///
-/// <https://github.com/w3c/csswg-drafts/issues/4650>
-#[derive(
- Clone,
- Debug,
- Default,
- Eq,
- MallocSizeOf,
- PartialEq,
- SpecifiedValueInfo,
- ToComputedValue,
- ToCss,
- ToResolvedValue,
- ToShmem,
-)]
-#[repr(transparent)]
-#[css(comma)]
-pub struct JustifyTracks(
- #[css(iterable, if_empty = "normal")] pub crate::OwnedSlice<JustifyContent>,
-);
-
-impl Parse for JustifyTracks {
- fn parse<'i, 't>(
- context: &ParserContext,
- input: &mut Parser<'i, 't>,
- ) -> Result<Self, ParseError<'i>> {
- let values = input.parse_comma_separated(|input| JustifyContent::parse(context, input))?;
- Ok(JustifyTracks(values.into()))
- }
-}
-
-/// <https://drafts.csswg.org/css-align/#self-alignment>
-#[derive(
- Clone,
- Copy,
- Debug,
- Eq,
- MallocSizeOf,
- PartialEq,
- ToComputedValue,
- ToCss,
- ToResolvedValue,
- ToShmem,
-)]
-#[repr(transparent)]
-pub struct SelfAlignment(pub AlignFlags);
-
-impl SelfAlignment {
- /// The initial value 'auto'
- #[inline]
- pub fn auto() -> Self {
- SelfAlignment(AlignFlags::AUTO)
- }
-
- /// Returns whether this value is valid for both axis directions.
- pub fn is_valid_on_both_axes(&self) -> bool {
- match self.0.value() {
- // left | right are only allowed on the inline axis.
- AlignFlags::LEFT | AlignFlags::RIGHT => false,
-
- _ => true,
- }
- }
-
- /// Parse a self-alignment value on one of the axis.
- pub fn parse<'i, 't>(
- input: &mut Parser<'i, 't>,
- axis: AxisDirection,
- ) -> Result<Self, ParseError<'i>> {
- // NOTE Please also update the `list_keywords` function below
- // when this function is updated.
-
- // <baseline-position>
- //
- // It's weird that this accepts <baseline-position>, but not
- // justify-content...
- if let Ok(value) = input.try_parse(parse_baseline) {
- return Ok(SelfAlignment(value));
- }
-
- // auto | normal | stretch
- if let Ok(value) = input.try_parse(parse_auto_normal_stretch) {
- return Ok(SelfAlignment(value));
- }
-
- // <overflow-position>? <self-position>
- let overflow_position = input
- .try_parse(parse_overflow_position)
- .unwrap_or(AlignFlags::empty());
- let self_position = parse_self_position(input, axis)?;
- Ok(SelfAlignment(overflow_position | self_position))
- }
-
- fn list_keywords(f: KeywordsCollectFn, axis: AxisDirection) {
- list_baseline_keywords(f);
- list_auto_normal_stretch(f);
- list_overflow_position_keywords(f);
- list_self_position_keywords(f, axis);
- }
-}
-
-/// The specified value of the align-self property.
-///
-/// <https://drafts.csswg.org/css-align/#propdef-align-self>
-#[derive(
- Clone,
- Copy,
- Debug,
- Eq,
- MallocSizeOf,
- PartialEq,
- ToComputedValue,
- ToCss,
- ToResolvedValue,
- ToShmem,
-)]
-#[repr(C)]
-pub struct AlignSelf(pub SelfAlignment);
-
-impl Parse for AlignSelf {
- fn parse<'i, 't>(
- _: &ParserContext,
- input: &mut Parser<'i, 't>,
- ) -> Result<Self, ParseError<'i>> {
- // NOTE Please also update `impl SpecifiedValueInfo` below when
- // this function is updated.
- Ok(AlignSelf(SelfAlignment::parse(
- input,
- AxisDirection::Block,
- )?))
- }
-}
-
-impl SpecifiedValueInfo for AlignSelf {
- fn collect_completion_keywords(f: KeywordsCollectFn) {
- SelfAlignment::list_keywords(f, AxisDirection::Block);
- }
-}
-
-/// The specified value of the justify-self property.
-///
-/// <https://drafts.csswg.org/css-align/#propdef-justify-self>
-#[derive(
- Clone,
- Copy,
- Debug,
- Eq,
- MallocSizeOf,
- PartialEq,
- ToComputedValue,
- ToCss,
- ToResolvedValue,
- ToShmem,
-)]
-#[repr(C)]
-pub struct JustifySelf(pub SelfAlignment);
-
-impl Parse for JustifySelf {
- fn parse<'i, 't>(
- _: &ParserContext,
- input: &mut Parser<'i, 't>,
- ) -> Result<Self, ParseError<'i>> {
- // NOTE Please also update `impl SpecifiedValueInfo` below when
- // this function is updated.
- Ok(JustifySelf(SelfAlignment::parse(
- input,
- AxisDirection::Inline,
- )?))
- }
-}
-
-impl SpecifiedValueInfo for JustifySelf {
- fn collect_completion_keywords(f: KeywordsCollectFn) {
- SelfAlignment::list_keywords(f, AxisDirection::Inline);
- }
-}
-
-/// Value of the `align-items` property
-///
-/// <https://drafts.csswg.org/css-align/#propdef-align-items>
-#[derive(
- Clone,
- Copy,
- Debug,
- Eq,
- MallocSizeOf,
- PartialEq,
- ToComputedValue,
- ToCss,
- ToResolvedValue,
- ToShmem,
-)]
-#[repr(C)]
-pub struct AlignItems(pub AlignFlags);
-
-impl AlignItems {
- /// The initial value 'normal'
- #[inline]
- pub fn normal() -> Self {
- AlignItems(AlignFlags::NORMAL)
- }
-}
-
-impl Parse for AlignItems {
- // normal | stretch | <baseline-position> |
- // <overflow-position>? <self-position>
- fn parse<'i, 't>(
- _: &ParserContext,
- input: &mut Parser<'i, 't>,
- ) -> Result<Self, ParseError<'i>> {
- // NOTE Please also update `impl SpecifiedValueInfo` below when
- // this function is updated.
-
- // <baseline-position>
- if let Ok(baseline) = input.try_parse(parse_baseline) {
- return Ok(AlignItems(baseline));
- }
-
- // normal | stretch
- if let Ok(value) = input.try_parse(parse_normal_stretch) {
- return Ok(AlignItems(value));
- }
- // <overflow-position>? <self-position>
- let overflow = input
- .try_parse(parse_overflow_position)
- .unwrap_or(AlignFlags::empty());
- let self_position = parse_self_position(input, AxisDirection::Block)?;
- Ok(AlignItems(self_position | overflow))
- }
-}
-
-impl SpecifiedValueInfo for AlignItems {
- fn collect_completion_keywords(f: KeywordsCollectFn) {
- list_baseline_keywords(f);
- list_normal_stretch(f);
- list_overflow_position_keywords(f);
- list_self_position_keywords(f, AxisDirection::Block);
- }
-}
-
-/// Value of the `justify-items` property
-///
-/// <https://drafts.csswg.org/css-align/#justify-items-property>
-#[derive(Clone, Copy, Debug, Eq, MallocSizeOf, PartialEq, ToCss, ToResolvedValue, ToShmem)]
-#[repr(C)]
-pub struct JustifyItems(pub AlignFlags);
-
-impl JustifyItems {
- /// The initial value 'legacy'
- #[inline]
- pub fn legacy() -> Self {
- JustifyItems(AlignFlags::LEGACY)
- }
-
- /// The value 'normal'
- #[inline]
- pub fn normal() -> Self {
- JustifyItems(AlignFlags::NORMAL)
- }
-}
-
-impl Parse for JustifyItems {
- fn parse<'i, 't>(
- _: &ParserContext,
- input: &mut Parser<'i, 't>,
- ) -> Result<Self, ParseError<'i>> {
- // NOTE Please also update `impl SpecifiedValueInfo` below when
- // this function is updated.
-
- // <baseline-position>
- //
- // It's weird that this accepts <baseline-position>, but not
- // justify-content...
- if let Ok(baseline) = input.try_parse(parse_baseline) {
- return Ok(JustifyItems(baseline));
- }
-
- // normal | stretch
- if let Ok(value) = input.try_parse(parse_normal_stretch) {
- return Ok(JustifyItems(value));
- }
-
- // legacy | [ legacy && [ left | right | center ] ]
- if let Ok(value) = input.try_parse(parse_legacy) {
- return Ok(JustifyItems(value));
- }
-
- // <overflow-position>? <self-position>
- let overflow = input
- .try_parse(parse_overflow_position)
- .unwrap_or(AlignFlags::empty());
- let self_position = parse_self_position(input, AxisDirection::Inline)?;
- Ok(JustifyItems(overflow | self_position))
- }
-}
-
-impl SpecifiedValueInfo for JustifyItems {
- fn collect_completion_keywords(f: KeywordsCollectFn) {
- list_baseline_keywords(f);
- list_normal_stretch(f);
- list_legacy_keywords(f);
- list_overflow_position_keywords(f);
- list_self_position_keywords(f, AxisDirection::Inline);
- }
-}
-
-// auto | normal | stretch
-fn parse_auto_normal_stretch<'i, 't>(
- input: &mut Parser<'i, 't>,
-) -> Result<AlignFlags, ParseError<'i>> {
- // NOTE Please also update the `list_auto_normal_stretch` function
- // below when this function is updated.
- try_match_ident_ignore_ascii_case! { input,
- "auto" => Ok(AlignFlags::AUTO),
- "normal" => Ok(AlignFlags::NORMAL),
- "stretch" => Ok(AlignFlags::STRETCH),
- }
-}
-
-fn list_auto_normal_stretch(f: KeywordsCollectFn) {
- f(&["auto", "normal", "stretch"]);
-}
-
-// normal | stretch
-fn parse_normal_stretch<'i, 't>(input: &mut Parser<'i, 't>) -> Result<AlignFlags, ParseError<'i>> {
- // NOTE Please also update the `list_normal_stretch` function below
- // when this function is updated.
- try_match_ident_ignore_ascii_case! { input,
- "normal" => Ok(AlignFlags::NORMAL),
- "stretch" => Ok(AlignFlags::STRETCH),
- }
-}
-
-fn list_normal_stretch(f: KeywordsCollectFn) {
- f(&["normal", "stretch"]);
-}
-
-// <baseline-position>
-fn parse_baseline<'i, 't>(input: &mut Parser<'i, 't>) -> Result<AlignFlags, ParseError<'i>> {
- // NOTE Please also update the `list_baseline_keywords` function
- // below when this function is updated.
- try_match_ident_ignore_ascii_case! { input,
- "baseline" => Ok(AlignFlags::BASELINE),
- "first" => {
- input.expect_ident_matching("baseline")?;
- Ok(AlignFlags::BASELINE)
- },
- "last" => {
- input.expect_ident_matching("baseline")?;
- Ok(AlignFlags::LAST_BASELINE)
- },
- }
-}
-
-fn list_baseline_keywords(f: KeywordsCollectFn) {
- f(&["baseline", "first baseline", "last baseline"]);
-}
-
-// <content-distribution>
-fn parse_content_distribution<'i, 't>(
- input: &mut Parser<'i, 't>,
-) -> Result<AlignFlags, ParseError<'i>> {
- // NOTE Please also update the `list_content_distribution_keywords`
- // function below when this function is updated.
- try_match_ident_ignore_ascii_case! { input,
- "stretch" => Ok(AlignFlags::STRETCH),
- "space-between" => Ok(AlignFlags::SPACE_BETWEEN),
- "space-around" => Ok(AlignFlags::SPACE_AROUND),
- "space-evenly" => Ok(AlignFlags::SPACE_EVENLY),
- }
-}
-
-fn list_content_distribution_keywords(f: KeywordsCollectFn) {
- f(&["stretch", "space-between", "space-around", "space-evenly"]);
-}
-
-// <overflow-position>
-fn parse_overflow_position<'i, 't>(
- input: &mut Parser<'i, 't>,
-) -> Result<AlignFlags, ParseError<'i>> {
- // NOTE Please also update the `list_overflow_position_keywords`
- // function below when this function is updated.
- try_match_ident_ignore_ascii_case! { input,
- "safe" => Ok(AlignFlags::SAFE),
- "unsafe" => Ok(AlignFlags::UNSAFE),
- }
-}
-
-fn list_overflow_position_keywords(f: KeywordsCollectFn) {
- f(&["safe", "unsafe"]);
-}
-
-// <self-position> | left | right in the inline axis.
-fn parse_self_position<'i, 't>(
- input: &mut Parser<'i, 't>,
- axis: AxisDirection,
-) -> Result<AlignFlags, ParseError<'i>> {
- // NOTE Please also update the `list_self_position_keywords`
- // function below when this function is updated.
- Ok(try_match_ident_ignore_ascii_case! { input,
- "start" => AlignFlags::START,
- "end" => AlignFlags::END,
- "flex-start" => AlignFlags::FLEX_START,
- "flex-end" => AlignFlags::FLEX_END,
- "center" => AlignFlags::CENTER,
- "self-start" => AlignFlags::SELF_START,
- "self-end" => AlignFlags::SELF_END,
- "left" if axis == AxisDirection::Inline => AlignFlags::LEFT,
- "right" if axis == AxisDirection::Inline => AlignFlags::RIGHT,
- })
-}
-
-fn list_self_position_keywords(f: KeywordsCollectFn, axis: AxisDirection) {
- f(&[
- "start",
- "end",
- "flex-start",
- "flex-end",
- "center",
- "self-start",
- "self-end",
- ]);
- if axis == AxisDirection::Inline {
- f(&["left", "right"]);
- }
-}
-
-fn parse_left_right_center<'i, 't>(
- input: &mut Parser<'i, 't>,
-) -> Result<AlignFlags, ParseError<'i>> {
- // NOTE Please also update the `list_legacy_keywords` function below
- // when this function is updated.
- Ok(try_match_ident_ignore_ascii_case! { input,
- "left" => AlignFlags::LEFT,
- "right" => AlignFlags::RIGHT,
- "center" => AlignFlags::CENTER,
- })
-}
-
-// legacy | [ legacy && [ left | right | center ] ]
-fn parse_legacy<'i, 't>(input: &mut Parser<'i, 't>) -> Result<AlignFlags, ParseError<'i>> {
- // NOTE Please also update the `list_legacy_keywords` function below
- // when this function is updated.
- let flags = try_match_ident_ignore_ascii_case! { input,
- "legacy" => {
- let flags = input.try_parse(parse_left_right_center)
- .unwrap_or(AlignFlags::empty());
-
- return Ok(AlignFlags::LEGACY | flags)
- },
- "left" => AlignFlags::LEFT,
- "right" => AlignFlags::RIGHT,
- "center" => AlignFlags::CENTER,
- };
-
- input.expect_ident_matching("legacy")?;
- Ok(AlignFlags::LEGACY | flags)
-}
-
-fn list_legacy_keywords(f: KeywordsCollectFn) {
- f(&["legacy", "left", "right", "center"]);
-}
diff --git a/components/style/values/specified/angle.rs b/components/style/values/specified/angle.rs
deleted file mode 100644
index fb4554eb852..00000000000
--- a/components/style/values/specified/angle.rs
+++ /dev/null
@@ -1,276 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
-
-//! Specified angles.
-
-use crate::parser::{Parse, ParserContext};
-use crate::values::computed::angle::Angle as ComputedAngle;
-use crate::values::computed::{Context, ToComputedValue};
-use crate::values::specified::calc::CalcNode;
-use crate::values::CSSFloat;
-use crate::Zero;
-use cssparser::{Parser, Token};
-use std::f32::consts::PI;
-use std::fmt::{self, Write};
-use style_traits::{CssWriter, ParseError, SpecifiedValueInfo, ToCss};
-
-/// A specified angle dimension.
-#[cfg_attr(feature = "servo", derive(Deserialize, Serialize))]
-#[derive(Clone, Copy, Debug, MallocSizeOf, PartialEq, PartialOrd, ToCss, ToShmem)]
-pub enum AngleDimension {
- /// An angle with degree unit.
- #[css(dimension)]
- Deg(CSSFloat),
- /// An angle with gradian unit.
- #[css(dimension)]
- Grad(CSSFloat),
- /// An angle with radian unit.
- #[css(dimension)]
- Rad(CSSFloat),
- /// An angle with turn unit.
- #[css(dimension)]
- Turn(CSSFloat),
-}
-
-impl Zero for AngleDimension {
- fn zero() -> Self {
- AngleDimension::Deg(0.)
- }
-
- fn is_zero(&self) -> bool {
- self.unitless_value() == 0.0
- }
-}
-
-impl AngleDimension {
- /// Returns the amount of degrees this angle represents.
- #[inline]
- fn degrees(&self) -> CSSFloat {
- const DEG_PER_RAD: f32 = 180.0 / PI;
- const DEG_PER_TURN: f32 = 360.0;
- const DEG_PER_GRAD: f32 = 180.0 / 200.0;
-
- match *self {
- AngleDimension::Deg(d) => d,
- AngleDimension::Rad(rad) => rad * DEG_PER_RAD,
- AngleDimension::Turn(turns) => turns * DEG_PER_TURN,
- AngleDimension::Grad(gradians) => gradians * DEG_PER_GRAD,
- }
- }
-
- fn unitless_value(&self) -> CSSFloat {
- match *self {
- AngleDimension::Deg(v) |
- AngleDimension::Rad(v) |
- AngleDimension::Turn(v) |
- AngleDimension::Grad(v) => v,
- }
- }
-
- fn unit(&self) -> &'static str {
- match *self {
- AngleDimension::Deg(_) => "deg",
- AngleDimension::Rad(_) => "rad",
- AngleDimension::Turn(_) => "turn",
- AngleDimension::Grad(_) => "grad",
- }
- }
-}
-
-/// A specified Angle value, which is just the angle dimension, plus whether it
-/// was specified as `calc()` or not.
-#[cfg_attr(feature = "servo", derive(Deserialize, Serialize))]
-#[derive(Clone, Copy, Debug, MallocSizeOf, PartialEq, ToShmem)]
-pub struct Angle {
- value: AngleDimension,
- was_calc: bool,
-}
-
-impl Zero for Angle {
- fn zero() -> Self {
- Self {
- value: Zero::zero(),
- was_calc: false,
- }
- }
-
- fn is_zero(&self) -> bool {
- self.value.is_zero()
- }
-}
-
-impl ToCss for Angle {
- fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
- where
- W: Write,
- {
- crate::values::serialize_specified_dimension(
- self.value.unitless_value(),
- self.value.unit(),
- self.was_calc,
- dest,
- )
- }
-}
-
-impl ToComputedValue for Angle {
- type ComputedValue = ComputedAngle;
-
- #[inline]
- fn to_computed_value(&self, _context: &Context) -> Self::ComputedValue {
- let degrees = self.degrees();
-
- // NaN and +-infinity should degenerate to 0: https://github.com/w3c/csswg-drafts/issues/6105
- ComputedAngle::from_degrees(if degrees.is_finite() { degrees } else { 0.0 })
- }
-
- #[inline]
- fn from_computed_value(computed: &Self::ComputedValue) -> Self {
- Angle {
- value: AngleDimension::Deg(computed.degrees()),
- was_calc: false,
- }
- }
-}
-
-impl Angle {
- /// Creates an angle with the given value in degrees.
- #[inline]
- pub fn from_degrees(value: CSSFloat, was_calc: bool) -> Self {
- Angle {
- value: AngleDimension::Deg(value),
- was_calc,
- }
- }
-
- /// Creates an angle with the given value in radians.
- #[inline]
- pub fn from_radians(value: CSSFloat) -> Self {
- Angle {
- value: AngleDimension::Rad(value),
- was_calc: false,
- }
- }
-
- /// Return `0deg`.
- pub fn zero() -> Self {
- Self::from_degrees(0.0, false)
- }
-
- /// Returns the value of the angle in degrees, mostly for `calc()`.
- #[inline]
- pub fn degrees(&self) -> CSSFloat {
- self.value.degrees()
- }
-
- /// Returns the value of the angle in radians.
- #[inline]
- pub fn radians(&self) -> CSSFloat {
- const RAD_PER_DEG: f32 = PI / 180.0;
- self.value.degrees() * RAD_PER_DEG
- }
-
- /// Whether this specified angle came from a `calc()` expression.
- #[inline]
- pub fn was_calc(&self) -> bool {
- self.was_calc
- }
-
- /// Returns an `Angle` parsed from a `calc()` expression.
- pub fn from_calc(degrees: CSSFloat) -> Self {
- Angle {
- value: AngleDimension::Deg(degrees),
- was_calc: true,
- }
- }
-
- /// Returns the unit of the angle.
- #[inline]
- pub fn unit(&self) -> &'static str {
- self.value.unit()
- }
-}
-
-/// Whether to allow parsing an unitless zero as a valid angle.
-///
-/// This should always be `No`, except for exceptions like:
-///
-/// https://github.com/w3c/fxtf-drafts/issues/228
-///
-/// See also: https://github.com/w3c/csswg-drafts/issues/1162.
-#[allow(missing_docs)]
-pub enum AllowUnitlessZeroAngle {
- Yes,
- No,
-}
-
-impl Parse for Angle {
- /// Parses an angle according to CSS-VALUES § 6.1.
- fn parse<'i, 't>(
- context: &ParserContext,
- input: &mut Parser<'i, 't>,
- ) -> Result<Self, ParseError<'i>> {
- Self::parse_internal(context, input, AllowUnitlessZeroAngle::No)
- }
-}
-
-impl Angle {
- /// Parse an `<angle>` value given a value and an unit.
- pub fn parse_dimension(value: CSSFloat, unit: &str, was_calc: bool) -> Result<Angle, ()> {
- let value = match_ignore_ascii_case! { unit,
- "deg" => AngleDimension::Deg(value),
- "grad" => AngleDimension::Grad(value),
- "turn" => AngleDimension::Turn(value),
- "rad" => AngleDimension::Rad(value),
- _ => return Err(())
- };
-
- Ok(Self { value, was_calc })
- }
-
- /// Parse an `<angle>` allowing unitless zero to represent a zero angle.
- ///
- /// See the comment in `AllowUnitlessZeroAngle` for why.
- #[inline]
- pub fn parse_with_unitless<'i, 't>(
- context: &ParserContext,
- input: &mut Parser<'i, 't>,
- ) -> Result<Self, ParseError<'i>> {
- Self::parse_internal(context, input, AllowUnitlessZeroAngle::Yes)
- }
-
- pub(super) fn parse_internal<'i, 't>(
- context: &ParserContext,
- input: &mut Parser<'i, 't>,
- allow_unitless_zero: AllowUnitlessZeroAngle,
- ) -> Result<Self, ParseError<'i>> {
- let location = input.current_source_location();
- let t = input.next()?;
- let allow_unitless_zero = matches!(allow_unitless_zero, AllowUnitlessZeroAngle::Yes);
- match *t {
- Token::Dimension {
- value, ref unit, ..
- } => {
- match Angle::parse_dimension(value, unit, /* from_calc = */ false) {
- Ok(angle) => Ok(angle),
- Err(()) => {
- let t = t.clone();
- Err(input.new_unexpected_token_error(t))
- },
- }
- },
- Token::Function(ref name) => {
- let function = CalcNode::math_function(context, name, location)?;
- CalcNode::parse_angle(context, input, function)
- },
- Token::Number { value, .. } if value == 0. && allow_unitless_zero => Ok(Angle::zero()),
- ref t => {
- let t = t.clone();
- Err(input.new_unexpected_token_error(t))
- },
- }
- }
-}
-
-impl SpecifiedValueInfo for Angle {}
diff --git a/components/style/values/specified/animation.rs b/components/style/values/specified/animation.rs
deleted file mode 100644
index ad4fbc587c1..00000000000
--- a/components/style/values/specified/animation.rs
+++ /dev/null
@@ -1,420 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
-
-//! Specified types for properties related to animations and transitions.
-
-use crate::custom_properties::Name as CustomPropertyName;
-use crate::parser::{Parse, ParserContext};
-use crate::properties::{LonghandId, PropertyDeclarationId, PropertyId, ShorthandId};
-use crate::values::generics::animation as generics;
-use crate::values::specified::{LengthPercentage, NonNegativeNumber};
-use crate::values::{CustomIdent, KeyframesName, TimelineName};
-use crate::Atom;
-use cssparser::Parser;
-use std::fmt::{self, Write};
-use style_traits::{
- CssWriter, KeywordsCollectFn, ParseError, SpecifiedValueInfo, StyleParseErrorKind, ToCss,
-};
-
-/// A given transition property, that is either `All`, a longhand or shorthand
-/// property, or an unsupported or custom property.
-#[derive(
- Clone, Debug, Eq, Hash, MallocSizeOf, PartialEq, ToComputedValue, ToResolvedValue, ToShmem,
-)]
-pub enum TransitionProperty {
- /// A shorthand.
- Shorthand(ShorthandId),
- /// A longhand transitionable property.
- Longhand(LonghandId),
- /// A custom property.
- Custom(CustomPropertyName),
- /// Unrecognized property which could be any non-transitionable, custom property, or
- /// unknown property.
- Unsupported(CustomIdent),
-}
-
-impl ToCss for TransitionProperty {
- fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
- where
- W: Write,
- {
- use crate::values::serialize_atom_name;
- match *self {
- TransitionProperty::Shorthand(ref s) => s.to_css(dest),
- TransitionProperty::Longhand(ref l) => l.to_css(dest),
- TransitionProperty::Custom(ref name) => {
- dest.write_str("--")?;
- serialize_atom_name(name, dest)
- },
- TransitionProperty::Unsupported(ref i) => i.to_css(dest),
- }
- }
-}
-
-impl Parse for TransitionProperty {
- fn parse<'i, 't>(
- context: &ParserContext,
- input: &mut Parser<'i, 't>,
- ) -> Result<Self, ParseError<'i>> {
- let location = input.current_source_location();
- let ident = input.expect_ident()?;
-
- let id = match PropertyId::parse_ignoring_rule_type(&ident, context) {
- Ok(id) => id,
- Err(..) => {
- return Ok(TransitionProperty::Unsupported(CustomIdent::from_ident(
- location,
- ident,
- &["none"],
- )?));
- },
- };
-
- Ok(match id.as_shorthand() {
- Ok(s) => TransitionProperty::Shorthand(s),
- Err(longhand_or_custom) => match longhand_or_custom {
- PropertyDeclarationId::Longhand(id) => TransitionProperty::Longhand(id),
- PropertyDeclarationId::Custom(custom) => TransitionProperty::Custom(custom.clone()),
- },
- })
- }
-}
-
-impl SpecifiedValueInfo for TransitionProperty {
- fn collect_completion_keywords(f: KeywordsCollectFn) {
- // `transition-property` can actually accept all properties and
- // arbitrary identifiers, but `all` is a special one we'd like
- // to list.
- f(&["all"]);
- }
-}
-
-impl TransitionProperty {
- /// Returns `all`.
- #[inline]
- pub fn all() -> Self {
- TransitionProperty::Shorthand(ShorthandId::All)
- }
-
- /// Convert TransitionProperty to nsCSSPropertyID.
- #[cfg(feature = "gecko")]
- pub fn to_nscsspropertyid(
- &self,
- ) -> Result<crate::gecko_bindings::structs::nsCSSPropertyID, ()> {
- Ok(match *self {
- TransitionProperty::Shorthand(ShorthandId::All) => {
- crate::gecko_bindings::structs::nsCSSPropertyID::eCSSPropertyExtra_all_properties
- },
- TransitionProperty::Shorthand(ref id) => id.to_nscsspropertyid(),
- TransitionProperty::Longhand(ref id) => id.to_nscsspropertyid(),
- TransitionProperty::Custom(..) | TransitionProperty::Unsupported(..) => return Err(()),
- })
- }
-}
-
-/// https://drafts.csswg.org/css-animations/#animation-iteration-count
-#[derive(Clone, Debug, MallocSizeOf, PartialEq, Parse, SpecifiedValueInfo, ToCss, ToShmem)]
-pub enum AnimationIterationCount {
- /// A `<number>` value.
- Number(NonNegativeNumber),
- /// The `infinite` keyword.
- Infinite,
-}
-
-impl AnimationIterationCount {
- /// Returns the value `1.0`.
- #[inline]
- pub fn one() -> Self {
- Self::Number(NonNegativeNumber::new(1.0))
- }
-}
-
-/// A value for the `animation-name` property.
-#[derive(
- Clone,
- Debug,
- Eq,
- Hash,
- MallocSizeOf,
- PartialEq,
- SpecifiedValueInfo,
- ToComputedValue,
- ToCss,
- ToResolvedValue,
- ToShmem,
-)]
-#[value_info(other_values = "none")]
-#[repr(C)]
-pub struct AnimationName(pub KeyframesName);
-
-impl AnimationName {
- /// Get the name of the animation as an `Atom`.
- pub fn as_atom(&self) -> Option<&Atom> {
- if self.is_none() {
- return None;
- }
- Some(self.0.as_atom())
- }
-
- /// Returns the `none` value.
- pub fn none() -> Self {
- AnimationName(KeyframesName::none())
- }
-
- /// Returns whether this is the none value.
- pub fn is_none(&self) -> bool {
- self.0.is_none()
- }
-}
-
-impl Parse for AnimationName {
- fn parse<'i, 't>(
- context: &ParserContext,
- input: &mut Parser<'i, 't>,
- ) -> Result<Self, ParseError<'i>> {
- if let Ok(name) = input.try_parse(|input| KeyframesName::parse(context, input)) {
- return Ok(AnimationName(name));
- }
-
- input.expect_ident_matching("none")?;
- Ok(AnimationName(KeyframesName::none()))
- }
-}
-
-/// A value for the <Scroller> used in scroll().
-///
-/// https://drafts.csswg.org/scroll-animations-1/rewrite#typedef-scroller
-#[derive(
- Clone,
- Debug,
- Eq,
- Hash,
- MallocSizeOf,
- Parse,
- PartialEq,
- SpecifiedValueInfo,
- ToComputedValue,
- ToCss,
- ToResolvedValue,
- ToShmem,
-)]
-#[repr(u8)]
-pub enum Scroller {
- /// The nearest ancestor scroll container. (Default.)
- Nearest,
- /// The document viewport as the scroll container.
- Root,
- /// Specifies to use the element’s own principal box as the scroll container.
- #[css(keyword = "self")]
- SelfElement,
-}
-
-impl Scroller {
- /// Returns true if it is default.
- #[inline]
- fn is_default(&self) -> bool {
- matches!(*self, Self::Nearest)
- }
-}
-
-impl Default for Scroller {
- fn default() -> Self {
- Self::Nearest
- }
-}
-
-/// A value for the <Axis> used in scroll(), or a value for {scroll|view}-timeline-axis.
-///
-/// https://drafts.csswg.org/scroll-animations-1/#typedef-axis
-/// https://drafts.csswg.org/scroll-animations-1/#scroll-timeline-axis
-/// https://drafts.csswg.org/scroll-animations-1/#view-timeline-axis
-#[derive(
- Clone,
- Debug,
- Eq,
- Hash,
- MallocSizeOf,
- Parse,
- PartialEq,
- SpecifiedValueInfo,
- ToComputedValue,
- ToCss,
- ToResolvedValue,
- ToShmem,
-)]
-#[repr(u8)]
-pub enum ScrollAxis {
- /// The block axis of the scroll container. (Default.)
- Block = 0,
- /// The inline axis of the scroll container.
- Inline = 1,
- /// The vertical block axis of the scroll container.
- Vertical = 2,
- /// The horizontal axis of the scroll container.
- Horizontal = 3,
-}
-
-impl ScrollAxis {
- /// Returns true if it is default.
- #[inline]
- pub fn is_default(&self) -> bool {
- matches!(*self, Self::Block)
- }
-}
-
-impl Default for ScrollAxis {
- fn default() -> Self {
- Self::Block
- }
-}
-
-/// The scroll() notation.
-/// https://drafts.csswg.org/scroll-animations-1/#scroll-notation
-#[derive(
- Clone,
- Debug,
- MallocSizeOf,
- PartialEq,
- SpecifiedValueInfo,
- ToComputedValue,
- ToCss,
- ToResolvedValue,
- ToShmem,
-)]
-#[css(function = "scroll")]
-#[repr(C)]
-pub struct ScrollFunction {
- /// The scroll container element whose scroll position drives the progress of the timeline.
- #[css(skip_if = "Scroller::is_default")]
- pub scroller: Scroller,
- /// The axis of scrolling that drives the progress of the timeline.
- #[css(skip_if = "ScrollAxis::is_default")]
- pub axis: ScrollAxis,
-}
-
-impl ScrollFunction {
- /// Parse the inner function arguments of `scroll()`.
- fn parse_arguments<'i, 't>(input: &mut Parser<'i, 't>) -> Result<Self, ParseError<'i>> {
- // <scroll()> = scroll( [ <scroller> || <axis> ]? )
- // https://drafts.csswg.org/scroll-animations-1/#funcdef-scroll
- let mut scroller = None;
- let mut axis = None;
- loop {
- if scroller.is_none() {
- scroller = input.try_parse(Scroller::parse).ok();
- }
-
- if axis.is_none() {
- axis = input.try_parse(ScrollAxis::parse).ok();
- if axis.is_some() {
- continue;
- }
- }
- break;
- }
-
- Ok(Self {
- scroller: scroller.unwrap_or_default(),
- axis: axis.unwrap_or_default(),
- })
- }
-}
-
-impl generics::ViewFunction<LengthPercentage> {
- /// Parse the inner function arguments of `view()`.
- fn parse_arguments<'i, 't>(
- context: &ParserContext,
- input: &mut Parser<'i, 't>,
- ) -> Result<Self, ParseError<'i>> {
- // <view()> = view( [ <axis> || <'view-timeline-inset'> ]? )
- // https://drafts.csswg.org/scroll-animations-1/#funcdef-view
- let mut axis = None;
- let mut inset = None;
- loop {
- if axis.is_none() {
- axis = input.try_parse(ScrollAxis::parse).ok();
- }
-
- if inset.is_none() {
- inset = input
- .try_parse(|i| ViewTimelineInset::parse(context, i))
- .ok();
- if inset.is_some() {
- continue;
- }
- }
- break;
- }
-
- Ok(Self {
- inset: inset.unwrap_or_default(),
- axis: axis.unwrap_or_default(),
- })
- }
-}
-
-/// A specified value for the `animation-timeline` property.
-pub type AnimationTimeline = generics::GenericAnimationTimeline<LengthPercentage>;
-
-impl Parse for AnimationTimeline {
- fn parse<'i, 't>(
- context: &ParserContext,
- input: &mut Parser<'i, 't>,
- ) -> Result<Self, ParseError<'i>> {
- use crate::values::generics::animation::ViewFunction;
-
- // <single-animation-timeline> = auto | none | <custom-ident> | <scroll()> | <view()>
- // https://drafts.csswg.org/css-animations-2/#typedef-single-animation-timeline
-
- if input.try_parse(|i| i.expect_ident_matching("auto")).is_ok() {
- return Ok(Self::Auto);
- }
-
- if input.try_parse(|i| i.expect_ident_matching("none")).is_ok() {
- return Ok(AnimationTimeline::Timeline(TimelineName::none()));
- }
-
- if let Ok(name) = input.try_parse(|i| TimelineName::parse(context, i)) {
- return Ok(AnimationTimeline::Timeline(name));
- }
-
- // Parse possible functions
- let location = input.current_source_location();
- let function = input.expect_function()?.clone();
- input.parse_nested_block(move |i| {
- match_ignore_ascii_case! { &function,
- "scroll" => ScrollFunction::parse_arguments(i).map(Self::Scroll),
- "view" => ViewFunction::parse_arguments(context, i).map(Self::View),
- _ => {
- Err(location.new_custom_error(
- StyleParseErrorKind::UnexpectedFunction(function.clone())
- ))
- },
- }
- })
- }
-}
-
-/// A value for the scroll-timeline-name or view-timeline-name.
-pub type ScrollTimelineName = AnimationName;
-
-/// A specified value for the `view-timeline-inset` property.
-pub type ViewTimelineInset = generics::GenericViewTimelineInset<LengthPercentage>;
-
-impl Parse for ViewTimelineInset {
- fn parse<'i, 't>(
- context: &ParserContext,
- input: &mut Parser<'i, 't>,
- ) -> Result<Self, ParseError<'i>> {
- use crate::values::specified::LengthPercentageOrAuto;
-
- let start = LengthPercentageOrAuto::parse(context, input)?;
- let end = match input.try_parse(|input| LengthPercentageOrAuto::parse(context, input)) {
- Ok(end) => end,
- Err(_) => start.clone(),
- };
-
- Ok(Self { start, end })
- }
-}
diff --git a/components/style/values/specified/background.rs b/components/style/values/specified/background.rs
deleted file mode 100644
index 39a5a85193d..00000000000
--- a/components/style/values/specified/background.rs
+++ /dev/null
@@ -1,143 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
-
-//! Specified types for CSS values related to backgrounds.
-
-use crate::parser::{Parse, ParserContext};
-use crate::values::generics::background::BackgroundSize as GenericBackgroundSize;
-use crate::values::specified::length::{
- NonNegativeLengthPercentage, NonNegativeLengthPercentageOrAuto,
-};
-use cssparser::Parser;
-use selectors::parser::SelectorParseErrorKind;
-use std::fmt::{self, Write};
-use style_traits::{CssWriter, ParseError, ToCss};
-
-/// A specified value for the `background-size` property.
-pub type BackgroundSize = GenericBackgroundSize<NonNegativeLengthPercentage>;
-
-impl Parse for BackgroundSize {
- fn parse<'i, 't>(
- context: &ParserContext,
- input: &mut Parser<'i, 't>,
- ) -> Result<Self, ParseError<'i>> {
- if let Ok(width) = input.try_parse(|i| NonNegativeLengthPercentageOrAuto::parse(context, i))
- {
- let height = input
- .try_parse(|i| NonNegativeLengthPercentageOrAuto::parse(context, i))
- .unwrap_or(NonNegativeLengthPercentageOrAuto::auto());
- return Ok(GenericBackgroundSize::ExplicitSize { width, height });
- }
- Ok(try_match_ident_ignore_ascii_case! { input,
- "cover" => GenericBackgroundSize::Cover,
- "contain" => GenericBackgroundSize::Contain,
- })
- }
-}
-
-/// One of the keywords for `background-repeat`.
-#[derive(
- Clone,
- Copy,
- Debug,
- Eq,
- MallocSizeOf,
- Parse,
- PartialEq,
- SpecifiedValueInfo,
- ToComputedValue,
- ToCss,
- ToResolvedValue,
- ToShmem,
-)]
-#[allow(missing_docs)]
-#[value_info(other_values = "repeat-x,repeat-y")]
-pub enum BackgroundRepeatKeyword {
- Repeat,
- Space,
- Round,
- NoRepeat,
-}
-
-/// The value of the `background-repeat` property, with `repeat-x` / `repeat-y`
-/// represented as the combination of `no-repeat` and `repeat` in the opposite
-/// axes.
-///
-/// https://drafts.csswg.org/css-backgrounds/#the-background-repeat
-#[derive(
- Clone,
- Debug,
- MallocSizeOf,
- PartialEq,
- SpecifiedValueInfo,
- ToComputedValue,
- ToResolvedValue,
- ToShmem,
-)]
-pub struct BackgroundRepeat(pub BackgroundRepeatKeyword, pub BackgroundRepeatKeyword);
-
-impl BackgroundRepeat {
- /// Returns the `repeat repeat` value.
- pub fn repeat() -> Self {
- BackgroundRepeat(
- BackgroundRepeatKeyword::Repeat,
- BackgroundRepeatKeyword::Repeat,
- )
- }
-}
-
-impl ToCss for BackgroundRepeat {
- fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
- where
- W: Write,
- {
- match (self.0, self.1) {
- (BackgroundRepeatKeyword::Repeat, BackgroundRepeatKeyword::NoRepeat) => {
- dest.write_str("repeat-x")
- },
- (BackgroundRepeatKeyword::NoRepeat, BackgroundRepeatKeyword::Repeat) => {
- dest.write_str("repeat-y")
- },
- (horizontal, vertical) => {
- horizontal.to_css(dest)?;
- if horizontal != vertical {
- dest.write_char(' ')?;
- vertical.to_css(dest)?;
- }
- Ok(())
- },
- }
- }
-}
-
-impl Parse for BackgroundRepeat {
- fn parse<'i, 't>(
- _context: &ParserContext,
- input: &mut Parser<'i, 't>,
- ) -> Result<Self, ParseError<'i>> {
- let ident = input.expect_ident_cloned()?;
-
- match_ignore_ascii_case! { &ident,
- "repeat-x" => {
- return Ok(BackgroundRepeat(BackgroundRepeatKeyword::Repeat, BackgroundRepeatKeyword::NoRepeat));
- },
- "repeat-y" => {
- return Ok(BackgroundRepeat(BackgroundRepeatKeyword::NoRepeat, BackgroundRepeatKeyword::Repeat));
- },
- _ => {},
- }
-
- let horizontal = match BackgroundRepeatKeyword::from_ident(&ident) {
- Ok(h) => h,
- Err(()) => {
- return Err(
- input.new_custom_error(SelectorParseErrorKind::UnexpectedIdent(ident.clone()))
- );
- },
- };
-
- let vertical = input.try_parse(BackgroundRepeatKeyword::parse).ok();
- Ok(BackgroundRepeat(horizontal, vertical.unwrap_or(horizontal)))
- }
-}
diff --git a/components/style/values/specified/basic_shape.rs b/components/style/values/specified/basic_shape.rs
deleted file mode 100644
index c085446c642..00000000000
--- a/components/style/values/specified/basic_shape.rs
+++ /dev/null
@@ -1,321 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
-
-//! CSS handling for the specified value of
-//! [`basic-shape`][basic-shape]s
-//!
-//! [basic-shape]: https://drafts.csswg.org/css-shapes/#typedef-basic-shape
-
-use crate::parser::{Parse, ParserContext};
-use crate::values::generics::basic_shape as generic;
-use crate::values::generics::basic_shape::{Path, PolygonCoord};
-use crate::values::generics::rect::Rect;
-use crate::values::specified::border::BorderRadius;
-use crate::values::specified::image::Image;
-use crate::values::specified::position::{HorizontalPosition, Position, VerticalPosition};
-use crate::values::specified::url::SpecifiedUrl;
-use crate::values::specified::{LengthPercentage, NonNegativeLengthPercentage, SVGPathData};
-use crate::Zero;
-use cssparser::Parser;
-use style_traits::{ParseError, StyleParseErrorKind};
-
-/// A specified alias for FillRule.
-pub use crate::values::generics::basic_shape::FillRule;
-
-/// A specified `clip-path` value.
-pub type ClipPath = generic::GenericClipPath<BasicShape, SpecifiedUrl>;
-
-/// A specified `shape-outside` value.
-pub type ShapeOutside = generic::GenericShapeOutside<BasicShape, Image>;
-
-/// A specified basic shape.
-pub type BasicShape = generic::GenericBasicShape<
- HorizontalPosition,
- VerticalPosition,
- LengthPercentage,
- NonNegativeLengthPercentage,
->;
-
-/// The specified value of `inset()`
-pub type InsetRect = generic::InsetRect<LengthPercentage, NonNegativeLengthPercentage>;
-
-/// A specified circle.
-pub type Circle =
- generic::Circle<HorizontalPosition, VerticalPosition, NonNegativeLengthPercentage>;
-
-/// A specified ellipse.
-pub type Ellipse =
- generic::Ellipse<HorizontalPosition, VerticalPosition, NonNegativeLengthPercentage>;
-
-/// The specified value of `ShapeRadius`
-pub type ShapeRadius = generic::ShapeRadius<NonNegativeLengthPercentage>;
-
-/// The specified value of `Polygon`
-pub type Polygon = generic::GenericPolygon<LengthPercentage>;
-
-/// A helper for both clip-path and shape-outside parsing of shapes.
-fn parse_shape_or_box<'i, 't, R, ReferenceBox>(
- context: &ParserContext,
- input: &mut Parser<'i, 't>,
- to_shape: impl FnOnce(Box<BasicShape>, ReferenceBox) -> R,
- to_reference_box: impl FnOnce(ReferenceBox) -> R,
-) -> Result<R, ParseError<'i>>
-where
- ReferenceBox: Default + Parse,
-{
- fn parse_component<U: Parse>(
- context: &ParserContext,
- input: &mut Parser,
- component: &mut Option<U>,
- ) -> bool {
- if component.is_some() {
- return false; // already parsed this component
- }
-
- *component = input.try_parse(|i| U::parse(context, i)).ok();
- component.is_some()
- }
-
- let mut shape = None;
- let mut ref_box = None;
-
- while parse_component(context, input, &mut shape) ||
- parse_component(context, input, &mut ref_box)
- {
- //
- }
-
- if let Some(shp) = shape {
- return Ok(to_shape(Box::new(shp), ref_box.unwrap_or_default()));
- }
-
- match ref_box {
- Some(r) => Ok(to_reference_box(r)),
- None => Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError)),
- }
-}
-
-impl Parse for ClipPath {
- #[inline]
- fn parse<'i, 't>(
- context: &ParserContext,
- input: &mut Parser<'i, 't>,
- ) -> Result<Self, ParseError<'i>> {
- if input.try_parse(|i| i.expect_ident_matching("none")).is_ok() {
- return Ok(ClipPath::None);
- }
-
- if let Ok(p) = input.try_parse(|i| Path::parse(context, i)) {
- return Ok(ClipPath::Path(p));
- }
-
- if let Ok(url) = input.try_parse(|i| SpecifiedUrl::parse(context, i)) {
- return Ok(ClipPath::Url(url));
- }
-
- parse_shape_or_box(context, input, ClipPath::Shape, ClipPath::Box)
- }
-}
-
-impl Parse for ShapeOutside {
- #[inline]
- fn parse<'i, 't>(
- context: &ParserContext,
- input: &mut Parser<'i, 't>,
- ) -> Result<Self, ParseError<'i>> {
- // Need to parse this here so that `Image::parse_with_cors_anonymous`
- // doesn't parse it.
- if input.try_parse(|i| i.expect_ident_matching("none")).is_ok() {
- return Ok(ShapeOutside::None);
- }
-
- if let Ok(image) = input.try_parse(|i| Image::parse_with_cors_anonymous(context, i)) {
- debug_assert_ne!(image, Image::None);
- return Ok(ShapeOutside::Image(image));
- }
-
- parse_shape_or_box(context, input, ShapeOutside::Shape, ShapeOutside::Box)
- }
-}
-
-impl Parse for BasicShape {
- fn parse<'i, 't>(
- context: &ParserContext,
- input: &mut Parser<'i, 't>,
- ) -> Result<Self, ParseError<'i>> {
- let location = input.current_source_location();
- let function = input.expect_function()?.clone();
- input.parse_nested_block(move |i| {
- (match_ignore_ascii_case! { &function,
- "inset" => return InsetRect::parse_function_arguments(context, i).map(generic::BasicShape::Inset),
- "circle" => return Circle::parse_function_arguments(context, i).map(generic::BasicShape::Circle),
- "ellipse" => return Ellipse::parse_function_arguments(context, i).map(generic::BasicShape::Ellipse),
- "polygon" => return Polygon::parse_function_arguments(context, i).map(generic::BasicShape::Polygon),
- _ => Err(())
- }).map_err(|()| {
- location.new_custom_error(StyleParseErrorKind::UnexpectedFunction(function.clone()))
- })
- })
- }
-}
-
-impl Parse for InsetRect {
- fn parse<'i, 't>(
- context: &ParserContext,
- input: &mut Parser<'i, 't>,
- ) -> Result<Self, ParseError<'i>> {
- input.expect_function_matching("inset")?;
- input.parse_nested_block(|i| Self::parse_function_arguments(context, i))
- }
-}
-
-impl InsetRect {
- /// Parse the inner function arguments of `inset()`
- fn parse_function_arguments<'i, 't>(
- context: &ParserContext,
- input: &mut Parser<'i, 't>,
- ) -> Result<Self, ParseError<'i>> {
- let rect = Rect::parse_with(context, input, LengthPercentage::parse)?;
- let round = if input
- .try_parse(|i| i.expect_ident_matching("round"))
- .is_ok()
- {
- BorderRadius::parse(context, input)?
- } else {
- BorderRadius::zero()
- };
- Ok(generic::InsetRect { rect, round })
- }
-}
-
-impl Parse for Circle {
- fn parse<'i, 't>(
- context: &ParserContext,
- input: &mut Parser<'i, 't>,
- ) -> Result<Self, ParseError<'i>> {
- input.expect_function_matching("circle")?;
- input.parse_nested_block(|i| Self::parse_function_arguments(context, i))
- }
-}
-
-impl Circle {
- fn parse_function_arguments<'i, 't>(
- context: &ParserContext,
- input: &mut Parser<'i, 't>,
- ) -> Result<Self, ParseError<'i>> {
- let radius = input
- .try_parse(|i| ShapeRadius::parse(context, i))
- .unwrap_or_default();
- let position = if input.try_parse(|i| i.expect_ident_matching("at")).is_ok() {
- Position::parse(context, input)?
- } else {
- Position::center()
- };
-
- Ok(generic::Circle { radius, position })
- }
-}
-
-impl Parse for Ellipse {
- fn parse<'i, 't>(
- context: &ParserContext,
- input: &mut Parser<'i, 't>,
- ) -> Result<Self, ParseError<'i>> {
- input.expect_function_matching("ellipse")?;
- input.parse_nested_block(|i| Self::parse_function_arguments(context, i))
- }
-}
-
-impl Ellipse {
- fn parse_function_arguments<'i, 't>(
- context: &ParserContext,
- input: &mut Parser<'i, 't>,
- ) -> Result<Self, ParseError<'i>> {
- let (a, b) = input
- .try_parse(|i| -> Result<_, ParseError> {
- Ok((
- ShapeRadius::parse(context, i)?,
- ShapeRadius::parse(context, i)?,
- ))
- })
- .unwrap_or_default();
- let position = if input.try_parse(|i| i.expect_ident_matching("at")).is_ok() {
- Position::parse(context, input)?
- } else {
- Position::center()
- };
-
- Ok(generic::Ellipse {
- semiaxis_x: a,
- semiaxis_y: b,
- position: position,
- })
- }
-}
-
-impl Parse for Polygon {
- fn parse<'i, 't>(
- context: &ParserContext,
- input: &mut Parser<'i, 't>,
- ) -> Result<Self, ParseError<'i>> {
- input.expect_function_matching("polygon")?;
- input.parse_nested_block(|i| Self::parse_function_arguments(context, i))
- }
-}
-
-impl Polygon {
- /// Parse the inner arguments of a `polygon` function.
- fn parse_function_arguments<'i, 't>(
- context: &ParserContext,
- input: &mut Parser<'i, 't>,
- ) -> Result<Self, ParseError<'i>> {
- let fill = input
- .try_parse(|i| -> Result<_, ParseError> {
- let fill = FillRule::parse(i)?;
- i.expect_comma()?; // only eat the comma if there is something before it
- Ok(fill)
- })
- .unwrap_or_default();
-
- let coordinates = input
- .parse_comma_separated(|i| {
- Ok(PolygonCoord(
- LengthPercentage::parse(context, i)?,
- LengthPercentage::parse(context, i)?,
- ))
- })?
- .into();
-
- Ok(Polygon { fill, coordinates })
- }
-}
-
-impl Parse for Path {
- fn parse<'i, 't>(
- _context: &ParserContext,
- input: &mut Parser<'i, 't>,
- ) -> Result<Self, ParseError<'i>> {
- input.expect_function_matching("path")?;
- input.parse_nested_block(Self::parse_function_arguments)
- }
-}
-
-impl Path {
- /// Parse the inner arguments of a `path` function.
- fn parse_function_arguments<'i, 't>(
- input: &mut Parser<'i, 't>,
- ) -> Result<Self, ParseError<'i>> {
- use crate::values::specified::svg_path::AllowEmpty;
-
- let fill = input
- .try_parse(|i| -> Result<_, ParseError> {
- let fill = FillRule::parse(i)?;
- i.expect_comma()?;
- Ok(fill)
- })
- .unwrap_or_default();
- let path = SVGPathData::parse(input, AllowEmpty::No)?;
- Ok(Path { fill, path })
- }
-}
diff --git a/components/style/values/specified/border.rs b/components/style/values/specified/border.rs
deleted file mode 100644
index 35722bb8482..00000000000
--- a/components/style/values/specified/border.rs
+++ /dev/null
@@ -1,398 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
-
-//! Specified types for CSS values related to borders.
-
-use crate::parser::{Parse, ParserContext};
-use crate::values::computed::{Context, ToComputedValue};
-use crate::values::generics::border::BorderCornerRadius as GenericBorderCornerRadius;
-use crate::values::generics::border::BorderImageSideWidth as GenericBorderImageSideWidth;
-use crate::values::generics::border::BorderImageSlice as GenericBorderImageSlice;
-use crate::values::generics::border::BorderRadius as GenericBorderRadius;
-use crate::values::generics::border::BorderSpacing as GenericBorderSpacing;
-use crate::values::generics::rect::Rect;
-use crate::values::generics::size::Size2D;
-use crate::values::specified::length::{Length, NonNegativeLength, NonNegativeLengthPercentage};
-use crate::values::specified::{AllowQuirks, NonNegativeNumber, NonNegativeNumberOrPercentage};
-use crate::values::specified::Color;
-use crate::Zero;
-use app_units::Au;
-use cssparser::Parser;
-use std::fmt::{self, Write};
-use style_traits::{CssWriter, ParseError, ToCss, values::SequenceWriter};
-
-/// A specified value for a single side of a `border-style` property.
-///
-/// The order here corresponds to the integer values from the border conflict
-/// resolution rules in CSS 2.1 § 17.6.2.1. Higher values override lower values.
-#[allow(missing_docs)]
-#[cfg_attr(feature = "servo", derive(Deserialize, Serialize))]
-#[derive(
- Clone,
- Copy,
- Debug,
- Eq,
- FromPrimitive,
- MallocSizeOf,
- Ord,
- Parse,
- PartialEq,
- PartialOrd,
- SpecifiedValueInfo,
- ToComputedValue,
- ToCss,
- ToResolvedValue,
- ToShmem,
-)]
-#[repr(u8)]
-pub enum BorderStyle {
- Hidden,
- None,
- Inset,
- Groove,
- Outset,
- Ridge,
- Dotted,
- Dashed,
- Solid,
- Double,
-}
-
-impl BorderStyle {
- /// Whether this border style is either none or hidden.
- #[inline]
- pub fn none_or_hidden(&self) -> bool {
- matches!(*self, BorderStyle::None | BorderStyle::Hidden)
- }
-}
-
-/// A specified value for the `border-image-width` property.
-pub type BorderImageWidth = Rect<BorderImageSideWidth>;
-
-/// A specified value for a single side of a `border-image-width` property.
-pub type BorderImageSideWidth =
- GenericBorderImageSideWidth<NonNegativeLengthPercentage, NonNegativeNumber>;
-
-/// A specified value for the `border-image-slice` property.
-pub type BorderImageSlice = GenericBorderImageSlice<NonNegativeNumberOrPercentage>;
-
-/// A specified value for the `border-radius` property.
-pub type BorderRadius = GenericBorderRadius<NonNegativeLengthPercentage>;
-
-/// A specified value for the `border-*-radius` longhand properties.
-pub type BorderCornerRadius = GenericBorderCornerRadius<NonNegativeLengthPercentage>;
-
-/// A specified value for the `border-spacing` longhand properties.
-pub type BorderSpacing = GenericBorderSpacing<NonNegativeLength>;
-
-impl BorderImageSlice {
- /// Returns the `100%` value.
- #[inline]
- pub fn hundred_percent() -> Self {
- GenericBorderImageSlice {
- offsets: Rect::all(NonNegativeNumberOrPercentage::hundred_percent()),
- fill: false,
- }
- }
-}
-
-/// https://drafts.csswg.org/css-backgrounds-3/#typedef-line-width
-#[derive(Clone, Debug, MallocSizeOf, PartialEq, SpecifiedValueInfo, ToCss, ToShmem)]
-pub enum LineWidth {
- /// `thin`
- Thin,
- /// `medium`
- Medium,
- /// `thick`
- Thick,
- /// `<length>`
- Length(NonNegativeLength),
-}
-
-impl LineWidth {
- /// Returns the `0px` value.
- #[inline]
- pub fn zero() -> Self {
- Self::Length(NonNegativeLength::zero())
- }
-
- fn parse_quirky<'i, 't>(
- context: &ParserContext,
- input: &mut Parser<'i, 't>,
- allow_quirks: AllowQuirks,
- ) -> Result<Self, ParseError<'i>> {
- if let Ok(length) =
- input.try_parse(|i| NonNegativeLength::parse_quirky(context, i, allow_quirks))
- {
- return Ok(Self::Length(length));
- }
- Ok(try_match_ident_ignore_ascii_case! { input,
- "thin" => Self::Thin,
- "medium" => Self::Medium,
- "thick" => Self::Thick,
- })
- }
-}
-
-impl Parse for LineWidth {
- fn parse<'i>(
- context: &ParserContext,
- input: &mut Parser<'i, '_>,
- ) -> Result<Self, ParseError<'i>> {
- Self::parse_quirky(context, input, AllowQuirks::No)
- }
-}
-
-impl ToComputedValue for LineWidth {
- type ComputedValue = app_units::Au;
-
- #[inline]
- fn to_computed_value(&self, context: &Context) -> Self::ComputedValue {
- match *self {
- // https://drafts.csswg.org/css-backgrounds-3/#line-width
- Self::Thin => Au::from_px(1),
- Self::Medium => Au::from_px(3),
- Self::Thick => Au::from_px(5),
- Self::Length(ref length) => Au::from_f32_px(length.to_computed_value(context).px()),
- }
- }
-
- #[inline]
- fn from_computed_value(computed: &Self::ComputedValue) -> Self {
- Self::Length(NonNegativeLength::from_px(computed.to_f32_px()))
- }
-}
-
-/// A specified value for a single side of the `border-width` property. The difference between this
-/// and LineWidth is whether we snap to device pixels or not.
-#[derive(Clone, Debug, MallocSizeOf, PartialEq, SpecifiedValueInfo, ToCss, ToShmem)]
-pub struct BorderSideWidth(LineWidth);
-
-impl BorderSideWidth {
- /// Returns the `medium` value.
- pub fn medium() -> Self {
- Self(LineWidth::Medium)
- }
-
- /// Returns a bare px value from the argument.
- pub fn from_px(px: f32) -> Self {
- Self(LineWidth::Length(Length::from_px(px).into()))
- }
-
- /// Parses, with quirks.
- pub fn parse_quirky<'i, 't>(
- context: &ParserContext,
- input: &mut Parser<'i, 't>,
- allow_quirks: AllowQuirks,
- ) -> Result<Self, ParseError<'i>> {
- Ok(Self(LineWidth::parse_quirky(context, input, allow_quirks)?))
- }
-}
-
-impl Parse for BorderSideWidth {
- fn parse<'i>(
- context: &ParserContext,
- input: &mut Parser<'i, '_>,
- ) -> Result<Self, ParseError<'i>> {
- Self::parse_quirky(context, input, AllowQuirks::No)
- }
-}
-
-impl ToComputedValue for BorderSideWidth {
- type ComputedValue = app_units::Au;
-
- #[inline]
- fn to_computed_value(&self, context: &Context) -> Self::ComputedValue {
- let width = self.0.to_computed_value(context);
- // Round `width` down to the nearest device pixel, but any non-zero value that would round
- // down to zero is clamped to 1 device pixel.
- if width == Au(0) {
- return width;
- }
-
- let au_per_dev_px = context.device().app_units_per_device_pixel();
- std::cmp::max(
- Au(au_per_dev_px),
- Au(width.0 / au_per_dev_px * au_per_dev_px),
- )
- }
-
- #[inline]
- fn from_computed_value(computed: &Self::ComputedValue) -> Self {
- Self(LineWidth::from_computed_value(computed))
- }
-}
-
-impl BorderImageSideWidth {
- /// Returns `1`.
- #[inline]
- pub fn one() -> Self {
- GenericBorderImageSideWidth::Number(NonNegativeNumber::new(1.))
- }
-}
-
-impl Parse for BorderImageSlice {
- fn parse<'i, 't>(
- context: &ParserContext,
- input: &mut Parser<'i, 't>,
- ) -> Result<Self, ParseError<'i>> {
- let mut fill = input.try_parse(|i| i.expect_ident_matching("fill")).is_ok();
- let offsets = Rect::parse_with(context, input, NonNegativeNumberOrPercentage::parse)?;
- if !fill {
- fill = input.try_parse(|i| i.expect_ident_matching("fill")).is_ok();
- }
- Ok(GenericBorderImageSlice { offsets, fill })
- }
-}
-
-impl Parse for BorderRadius {
- fn parse<'i, 't>(
- context: &ParserContext,
- input: &mut Parser<'i, 't>,
- ) -> Result<Self, ParseError<'i>> {
- let widths = Rect::parse_with(context, input, NonNegativeLengthPercentage::parse)?;
- let heights = if input.try_parse(|i| i.expect_delim('/')).is_ok() {
- Rect::parse_with(context, input, NonNegativeLengthPercentage::parse)?
- } else {
- widths.clone()
- };
-
- Ok(GenericBorderRadius {
- top_left: BorderCornerRadius::new(widths.0, heights.0),
- top_right: BorderCornerRadius::new(widths.1, heights.1),
- bottom_right: BorderCornerRadius::new(widths.2, heights.2),
- bottom_left: BorderCornerRadius::new(widths.3, heights.3),
- })
- }
-}
-
-impl Parse for BorderCornerRadius {
- fn parse<'i, 't>(
- context: &ParserContext,
- input: &mut Parser<'i, 't>,
- ) -> Result<Self, ParseError<'i>> {
- Size2D::parse_with(context, input, NonNegativeLengthPercentage::parse)
- .map(GenericBorderCornerRadius)
- }
-}
-
-impl Parse for BorderSpacing {
- fn parse<'i, 't>(
- context: &ParserContext,
- input: &mut Parser<'i, 't>,
- ) -> Result<Self, ParseError<'i>> {
- Size2D::parse_with(context, input, |context, input| {
- NonNegativeLength::parse_quirky(context, input, AllowQuirks::Yes)
- })
- .map(GenericBorderSpacing)
- }
-}
-
-/// A single border-image-repeat keyword.
-#[allow(missing_docs)]
-#[cfg_attr(feature = "servo", derive(Deserialize, Serialize))]
-#[derive(
- Clone,
- Copy,
- Debug,
- Eq,
- MallocSizeOf,
- Parse,
- PartialEq,
- SpecifiedValueInfo,
- ToComputedValue,
- ToCss,
- ToResolvedValue,
- ToShmem,
-)]
-pub enum BorderImageRepeatKeyword {
- Stretch,
- Repeat,
- Round,
- Space,
-}
-
-/// The specified value for the `border-image-repeat` property.
-///
-/// https://drafts.csswg.org/css-backgrounds/#the-border-image-repeat
-#[derive(
- Clone,
- Copy,
- Debug,
- MallocSizeOf,
- PartialEq,
- SpecifiedValueInfo,
- ToComputedValue,
- ToResolvedValue,
- ToShmem,
-)]
-pub struct BorderImageRepeat(pub BorderImageRepeatKeyword, pub BorderImageRepeatKeyword);
-
-impl ToCss for BorderImageRepeat {
- fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
- where
- W: Write,
- {
- self.0.to_css(dest)?;
- if self.0 != self.1 {
- dest.write_char(' ')?;
- self.1.to_css(dest)?;
- }
- Ok(())
- }
-}
-
-impl BorderImageRepeat {
- /// Returns the `stretch` value.
- #[inline]
- pub fn stretch() -> Self {
- BorderImageRepeat(
- BorderImageRepeatKeyword::Stretch,
- BorderImageRepeatKeyword::Stretch,
- )
- }
-}
-
-impl Parse for BorderImageRepeat {
- fn parse<'i, 't>(
- _context: &ParserContext,
- input: &mut Parser<'i, 't>,
- ) -> Result<Self, ParseError<'i>> {
- let horizontal = BorderImageRepeatKeyword::parse(input)?;
- let vertical = input.try_parse(BorderImageRepeatKeyword::parse).ok();
- Ok(BorderImageRepeat(
- horizontal,
- vertical.unwrap_or(horizontal),
- ))
- }
-}
-
-/// Serializes a border shorthand value composed of width/style/color.
-pub fn serialize_directional_border<W>(
- dest: &mut CssWriter<W>,
- width: &BorderSideWidth,
- style: &BorderStyle,
- color: &Color,
-) -> fmt::Result
-where
- W: Write,
-{
- let has_style = *style != BorderStyle::None;
- let has_color = *color != Color::CurrentColor;
- let has_width = *width != BorderSideWidth::medium();
- if !has_style && !has_color && !has_width {
- return width.to_css(dest)
- }
- let mut writer = SequenceWriter::new(dest, " ");
- if has_width {
- writer.item(width)?;
- }
- if has_style {
- writer.item(style)?;
- }
- if has_color {
- writer.item(color)?;
- }
- Ok(())
-}
diff --git a/components/style/values/specified/box.rs b/components/style/values/specified/box.rs
deleted file mode 100644
index 7c41f1ead25..00000000000
--- a/components/style/values/specified/box.rs
+++ /dev/null
@@ -1,1879 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
-
-//! Specified types for box properties.
-
-use crate::parser::{Parse, ParserContext};
-#[cfg(feature = "gecko")]
-use crate::properties::{LonghandId, PropertyDeclarationId, PropertyId};
-use crate::values::generics::box_::{
- GenericContainIntrinsicSize, GenericLineClamp, GenericPerspective, GenericVerticalAlign,
- VerticalAlignKeyword,
-};
-use crate::values::specified::length::{LengthPercentage, NonNegativeLength};
-use crate::values::specified::{AllowQuirks, Integer};
-use crate::values::CustomIdent;
-use cssparser::Parser;
-use num_traits::FromPrimitive;
-use std::fmt::{self, Debug, Formatter, Write};
-use style_traits::{CssWriter, KeywordsCollectFn, ParseError};
-use style_traits::{SpecifiedValueInfo, StyleParseErrorKind, ToCss};
-
-#[cfg(not(feature = "servo"))]
-fn flexbox_enabled() -> bool {
- true
-}
-
-#[cfg(feature = "servo")]
-fn flexbox_enabled() -> bool {
- style_config::get_bool("layout.flexbox.enabled")
-}
-
-/// Defines an element’s display type, which consists of
-/// the two basic qualities of how an element generates boxes
-/// <https://drafts.csswg.org/css-display/#propdef-display>
-#[allow(missing_docs)]
-#[derive(Clone, Copy, Debug, Eq, FromPrimitive, Hash, MallocSizeOf, PartialEq, ToCss, ToShmem)]
-#[repr(u8)]
-pub enum DisplayOutside {
- None = 0,
- Inline,
- Block,
- TableCaption,
- InternalTable,
- #[cfg(feature = "gecko")]
- InternalRuby,
-}
-
-#[allow(missing_docs)]
-#[derive(Clone, Copy, Debug, Eq, FromPrimitive, Hash, MallocSizeOf, PartialEq, ToCss, ToShmem)]
-#[repr(u8)]
-pub enum DisplayInside {
- None = 0,
- Contents,
- Flow,
- FlowRoot,
- Flex,
- #[cfg(feature = "gecko")]
- Grid,
- Table,
- TableRowGroup,
- TableColumn,
- TableColumnGroup,
- TableHeaderGroup,
- TableFooterGroup,
- TableRow,
- TableCell,
- #[cfg(feature = "gecko")]
- Ruby,
- #[cfg(feature = "gecko")]
- RubyBase,
- #[cfg(feature = "gecko")]
- RubyBaseContainer,
- #[cfg(feature = "gecko")]
- RubyText,
- #[cfg(feature = "gecko")]
- RubyTextContainer,
- #[cfg(feature = "gecko")]
- WebkitBox,
-}
-
-#[allow(missing_docs)]
-#[derive(
- Clone,
- Copy,
- Eq,
- FromPrimitive,
- Hash,
- MallocSizeOf,
- PartialEq,
- ToComputedValue,
- ToResolvedValue,
- ToShmem,
-)]
-#[repr(transparent)]
-pub struct Display(u16);
-
-/// Gecko-only impl block for Display (shared stuff later in this file):
-#[allow(missing_docs)]
-#[allow(non_upper_case_globals)]
-impl Display {
- // Our u16 bits are used as follows: LOOOOOOOIIIIIIII
- const LIST_ITEM_BIT: u16 = 0x8000; //^
- const DISPLAY_OUTSIDE_BITS: u16 = 7; // ^^^^^^^
- const DISPLAY_INSIDE_BITS: u16 = 8; // ^^^^^^^^
-
- /// https://drafts.csswg.org/css-display/#the-display-properties
- pub const None: Self = Self::new(DisplayOutside::None, DisplayInside::None);
- pub const Contents: Self = Self::new(DisplayOutside::None, DisplayInside::Contents);
- pub const Inline: Self = Self::new(DisplayOutside::Inline, DisplayInside::Flow);
- pub const InlineBlock: Self = Self::new(DisplayOutside::Inline, DisplayInside::FlowRoot);
- pub const Block: Self = Self::new(DisplayOutside::Block, DisplayInside::Flow);
- #[cfg(feature = "gecko")]
- pub const FlowRoot: Self = Self::new(DisplayOutside::Block, DisplayInside::FlowRoot);
- pub const Flex: Self = Self::new(DisplayOutside::Block, DisplayInside::Flex);
- pub const InlineFlex: Self = Self::new(DisplayOutside::Inline, DisplayInside::Flex);
- #[cfg(feature = "gecko")]
- pub const Grid: Self = Self::new(DisplayOutside::Block, DisplayInside::Grid);
- #[cfg(feature = "gecko")]
- pub const InlineGrid: Self = Self::new(DisplayOutside::Inline, DisplayInside::Grid);
- pub const Table: Self = Self::new(DisplayOutside::Block, DisplayInside::Table);
- pub const InlineTable: Self = Self::new(DisplayOutside::Inline, DisplayInside::Table);
- pub const TableCaption: Self = Self::new(DisplayOutside::TableCaption, DisplayInside::Flow);
- #[cfg(feature = "gecko")]
- pub const Ruby: Self = Self::new(DisplayOutside::Inline, DisplayInside::Ruby);
- #[cfg(feature = "gecko")]
- pub const WebkitBox: Self = Self::new(DisplayOutside::Block, DisplayInside::WebkitBox);
- #[cfg(feature = "gecko")]
- pub const WebkitInlineBox: Self = Self::new(DisplayOutside::Inline, DisplayInside::WebkitBox);
-
- // Internal table boxes.
-
- pub const TableRowGroup: Self =
- Self::new(DisplayOutside::InternalTable, DisplayInside::TableRowGroup);
-
- pub const TableHeaderGroup: Self = Self::new(
- DisplayOutside::InternalTable,
- DisplayInside::TableHeaderGroup,
- );
-
- pub const TableFooterGroup: Self = Self::new(
- DisplayOutside::InternalTable,
- DisplayInside::TableFooterGroup,
- );
-
- pub const TableColumn: Self =
- Self::new(DisplayOutside::InternalTable, DisplayInside::TableColumn);
-
- pub const TableColumnGroup: Self = Self::new(
- DisplayOutside::InternalTable,
- DisplayInside::TableColumnGroup,
- );
-
- pub const TableRow: Self = Self::new(DisplayOutside::InternalTable, DisplayInside::TableRow);
-
- pub const TableCell: Self = Self::new(DisplayOutside::InternalTable, DisplayInside::TableCell);
-
- /// Internal ruby boxes.
- #[cfg(feature = "gecko")]
- pub const RubyBase: Self = Self::new(DisplayOutside::InternalRuby, DisplayInside::RubyBase);
- #[cfg(feature = "gecko")]
- pub const RubyBaseContainer: Self = Self::new(
- DisplayOutside::InternalRuby,
- DisplayInside::RubyBaseContainer,
- );
- #[cfg(feature = "gecko")]
- pub const RubyText: Self = Self::new(DisplayOutside::InternalRuby, DisplayInside::RubyText);
- #[cfg(feature = "gecko")]
- pub const RubyTextContainer: Self = Self::new(
- DisplayOutside::InternalRuby,
- DisplayInside::RubyTextContainer,
- );
-
- /// Make a raw display value from <display-outside> and <display-inside> values.
- #[inline]
- const fn new(outside: DisplayOutside, inside: DisplayInside) -> Self {
- let o: u16 = ((outside as u8) as u16) << Self::DISPLAY_INSIDE_BITS;
- let i: u16 = (inside as u8) as u16;
- Self(o | i)
- }
-
- /// Make a display enum value from <display-outside> and <display-inside> values.
- #[inline]
- fn from3(outside: DisplayOutside, inside: DisplayInside, list_item: bool) -> Self {
- let v = Self::new(outside, inside);
- if !list_item {
- return v;
- }
- Self(v.0 | Self::LIST_ITEM_BIT)
- }
-
- /// Accessor for the <display-inside> value.
- #[inline]
- pub fn inside(&self) -> DisplayInside {
- DisplayInside::from_u16(self.0 & ((1 << Self::DISPLAY_INSIDE_BITS) - 1)).unwrap()
- }
-
- /// Accessor for the <display-outside> value.
- #[inline]
- pub fn outside(&self) -> DisplayOutside {
- DisplayOutside::from_u16(
- (self.0 >> Self::DISPLAY_INSIDE_BITS) & ((1 << Self::DISPLAY_OUTSIDE_BITS) - 1),
- )
- .unwrap()
- }
-
- /// Returns the raw underlying u16 value.
- #[inline]
- pub const fn to_u16(&self) -> u16 {
- self.0
- }
-
- /// Whether this is `display: inline` (or `inline list-item`).
- #[inline]
- pub fn is_inline_flow(&self) -> bool {
- self.outside() == DisplayOutside::Inline && self.inside() == DisplayInside::Flow
- }
-
- /// Returns whether this `display` value is some kind of list-item.
- #[inline]
- pub const fn is_list_item(&self) -> bool {
- (self.0 & Self::LIST_ITEM_BIT) != 0
- }
-
- /// Returns whether this `display` value is a ruby level container.
- pub fn is_ruby_level_container(&self) -> bool {
- match *self {
- #[cfg(feature = "gecko")]
- Display::RubyBaseContainer | Display::RubyTextContainer => true,
- _ => false,
- }
- }
-
- /// Returns whether this `display` value is one of the types for ruby.
- pub fn is_ruby_type(&self) -> bool {
- match self.inside() {
- #[cfg(feature = "gecko")]
- DisplayInside::Ruby |
- DisplayInside::RubyBase |
- DisplayInside::RubyText |
- DisplayInside::RubyBaseContainer |
- DisplayInside::RubyTextContainer => true,
- _ => false,
- }
- }
-}
-
-/// Shared Display impl for both Gecko and Servo.
-#[allow(non_upper_case_globals)]
-impl Display {
- /// The initial display value.
- #[inline]
- pub fn inline() -> Self {
- Display::Inline
- }
-
- /// <https://drafts.csswg.org/css2/visuren.html#x13>
- #[cfg(feature = "servo")]
- #[inline]
- pub fn is_atomic_inline_level(&self) -> bool {
- match *self {
- Display::InlineBlock | Display::InlineFlex => true,
- Display::InlineTable => true,
- _ => false,
- }
- }
-
- /// Returns whether this `display` value is the display of a flex or
- /// grid container.
- ///
- /// This is used to implement various style fixups.
- pub fn is_item_container(&self) -> bool {
- match self.inside() {
- DisplayInside::Flex => true,
- #[cfg(feature = "gecko")]
- DisplayInside::Grid => true,
- _ => false,
- }
- }
-
- /// Returns whether an element with this display type is a line
- /// participant, which means it may lay its children on the same
- /// line as itself.
- pub fn is_line_participant(&self) -> bool {
- if self.is_inline_flow() {
- return true;
- }
- match *self {
- #[cfg(feature = "gecko")]
- Display::Contents | Display::Ruby | Display::RubyBaseContainer => true,
- _ => false,
- }
- }
-
- /// Convert this display into an equivalent block display.
- ///
- /// Also used for :root style adjustments.
- pub fn equivalent_block_display(&self, _is_root_element: bool) -> Self {
- {
- // Special handling for `contents` and `list-item`s on the root element.
- if _is_root_element && (self.is_contents() || self.is_list_item()) {
- return Display::Block;
- }
- }
-
- match self.outside() {
- DisplayOutside::Inline => {
- let inside = match self.inside() {
- // `inline-block` blockifies to `block` rather than
- // `flow-root`, for legacy reasons.
- DisplayInside::FlowRoot => DisplayInside::Flow,
- inside => inside,
- };
- Display::from3(DisplayOutside::Block, inside, self.is_list_item())
- },
- DisplayOutside::Block | DisplayOutside::None => *self,
- _ => Display::Block,
- }
- }
-
- /// Convert this display into an equivalent inline-outside display.
- /// https://drafts.csswg.org/css-display/#inlinify
- #[cfg(feature = "gecko")]
- pub fn inlinify(&self) -> Self {
- match self.outside() {
- DisplayOutside::Block => {
- let inside = match self.inside() {
- // `display: block` inlinifies to `display: inline-block`,
- // rather than `inline`, for legacy reasons.
- DisplayInside::Flow => DisplayInside::FlowRoot,
- inside => inside,
- };
- Display::from3(DisplayOutside::Inline, inside, self.is_list_item())
- },
- _ => *self,
- }
- }
-
- /// Returns true if the value is `Contents`
- #[inline]
- pub fn is_contents(&self) -> bool {
- match *self {
- Display::Contents => true,
- _ => false,
- }
- }
-
- /// Returns true if the value is `None`
- #[inline]
- pub fn is_none(&self) -> bool {
- *self == Display::None
- }
-}
-
-impl ToCss for Display {
- fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
- where
- W: fmt::Write,
- {
- let outside = self.outside();
- let inside = self.inside();
- match *self {
- Display::Block | Display::Inline => outside.to_css(dest),
- Display::InlineBlock => dest.write_str("inline-block"),
- #[cfg(feature = "gecko")]
- Display::WebkitInlineBox => dest.write_str("-webkit-inline-box"),
- Display::TableCaption => dest.write_str("table-caption"),
- _ => match (outside, inside) {
- #[cfg(feature = "gecko")]
- (DisplayOutside::Inline, DisplayInside::Grid) => dest.write_str("inline-grid"),
- (DisplayOutside::Inline, DisplayInside::Flex) => dest.write_str("inline-flex"),
- (DisplayOutside::Inline, DisplayInside::Table) => dest.write_str("inline-table"),
- #[cfg(feature = "gecko")]
- (DisplayOutside::Block, DisplayInside::Ruby) => dest.write_str("block ruby"),
- (_, inside) => {
- if self.is_list_item() {
- if outside != DisplayOutside::Block {
- outside.to_css(dest)?;
- dest.write_char(' ')?;
- }
- if inside != DisplayInside::Flow {
- inside.to_css(dest)?;
- dest.write_char(' ')?;
- }
- dest.write_str("list-item")
- } else {
- inside.to_css(dest)
- }
- },
- },
- }
- }
-}
-
-/// <display-inside> = flow | flow-root | table | flex | grid | ruby
-/// https://drafts.csswg.org/css-display/#typedef-display-inside
-fn parse_display_inside<'i, 't>(
- input: &mut Parser<'i, 't>,
-) -> Result<DisplayInside, ParseError<'i>> {
- Ok(try_match_ident_ignore_ascii_case! { input,
- "flow" => DisplayInside::Flow,
- "flex" if flexbox_enabled() => DisplayInside::Flex,
- "flow-root" => DisplayInside::FlowRoot,
- "table" => DisplayInside::Table,
- #[cfg(feature = "gecko")]
- "grid" => DisplayInside::Grid,
- #[cfg(feature = "gecko")]
- "ruby" => DisplayInside::Ruby,
- })
-}
-
-/// <display-outside> = block | inline | run-in
-/// https://drafts.csswg.org/css-display/#typedef-display-outside
-fn parse_display_outside<'i, 't>(
- input: &mut Parser<'i, 't>,
-) -> Result<DisplayOutside, ParseError<'i>> {
- Ok(try_match_ident_ignore_ascii_case! { input,
- "block" => DisplayOutside::Block,
- "inline" => DisplayOutside::Inline,
- // FIXME(bug 2056): not supported in layout yet:
- //"run-in" => DisplayOutside::RunIn,
- })
-}
-
-/// (flow | flow-root)?
-fn parse_display_inside_for_list_item<'i, 't>(
- input: &mut Parser<'i, 't>,
-) -> Result<DisplayInside, ParseError<'i>> {
- Ok(try_match_ident_ignore_ascii_case! { input,
- "flow" => DisplayInside::Flow,
- #[cfg(feature = "gecko")]
- "flow-root" => DisplayInside::FlowRoot,
- })
-}
-/// Test a <display-inside> Result for same values as above.
-fn is_valid_inside_for_list_item<'i>(inside: &Result<DisplayInside, ParseError<'i>>) -> bool {
- match inside {
- Ok(DisplayInside::Flow) => true,
- #[cfg(feature = "gecko")]
- Ok(DisplayInside::FlowRoot) => true,
- _ => false,
- }
-}
-
-/// Parse `list-item`.
-fn parse_list_item<'i, 't>(input: &mut Parser<'i, 't>) -> Result<(), ParseError<'i>> {
- Ok(input.expect_ident_matching("list-item")?)
-}
-
-impl Parse for Display {
- #[allow(unused)] // `context` isn't used for servo-2020 for now
- fn parse<'i, 't>(
- context: &ParserContext,
- input: &mut Parser<'i, 't>,
- ) -> Result<Display, ParseError<'i>> {
- // Parse all combinations of <display-inside/outside>? and `list-item`? first.
- let mut got_list_item = input.try_parse(parse_list_item).is_ok();
- let mut inside = if got_list_item {
- input.try_parse(parse_display_inside_for_list_item)
- } else {
- input.try_parse(parse_display_inside)
- };
- // <display-listitem> = <display-outside>? && [ flow | flow-root ]? && list-item
- // https://drafts.csswg.org/css-display/#typedef-display-listitem
- if !got_list_item && is_valid_inside_for_list_item(&inside) {
- got_list_item = input.try_parse(parse_list_item).is_ok();
- }
- let outside = input.try_parse(parse_display_outside);
- if outside.is_ok() {
- if !got_list_item && (inside.is_err() || is_valid_inside_for_list_item(&inside)) {
- got_list_item = input.try_parse(parse_list_item).is_ok();
- }
- if inside.is_err() {
- inside = if got_list_item {
- input.try_parse(parse_display_inside_for_list_item)
- } else {
- input.try_parse(parse_display_inside)
- };
- if !got_list_item && is_valid_inside_for_list_item(&inside) {
- got_list_item = input.try_parse(parse_list_item).is_ok();
- }
- }
- }
- if got_list_item || inside.is_ok() || outside.is_ok() {
- let inside = inside.unwrap_or(DisplayInside::Flow);
- let outside = outside.unwrap_or(match inside {
- // "If <display-outside> is omitted, the element’s outside display type
- // defaults to block — except for ruby, which defaults to inline."
- // https://drafts.csswg.org/css-display/#inside-model
- #[cfg(feature = "gecko")]
- DisplayInside::Ruby => DisplayOutside::Inline,
- _ => DisplayOutside::Block,
- });
- return Ok(Display::from3(outside, inside, got_list_item));
- }
-
- // Now parse the single-keyword `display` values.
- Ok(try_match_ident_ignore_ascii_case! { input,
- "none" => Display::None,
- "contents" => Display::Contents,
- "inline-block" => Display::InlineBlock,
- "inline-table" => Display::InlineTable,
- "-webkit-flex" if flexbox_enabled() => Display::Flex,
- "inline-flex" | "-webkit-inline-flex" if flexbox_enabled() => Display::InlineFlex,
- #[cfg(feature = "gecko")]
- "inline-grid" => Display::InlineGrid,
- "table-caption" => Display::TableCaption,
- "table-row-group" => Display::TableRowGroup,
- "table-header-group" => Display::TableHeaderGroup,
- "table-footer-group" => Display::TableFooterGroup,
- "table-column" => Display::TableColumn,
- "table-column-group" => Display::TableColumnGroup,
- "table-row" => Display::TableRow,
- "table-cell" => Display::TableCell,
- #[cfg(feature = "gecko")]
- "ruby-base" => Display::RubyBase,
- #[cfg(feature = "gecko")]
- "ruby-base-container" => Display::RubyBaseContainer,
- #[cfg(feature = "gecko")]
- "ruby-text" => Display::RubyText,
- #[cfg(feature = "gecko")]
- "ruby-text-container" => Display::RubyTextContainer,
- #[cfg(feature = "gecko")]
- "-webkit-box" => Display::WebkitBox,
- #[cfg(feature = "gecko")]
- "-webkit-inline-box" => Display::WebkitInlineBox,
- })
- }
-}
-
-impl SpecifiedValueInfo for Display {
- fn collect_completion_keywords(f: KeywordsCollectFn) {
- f(&[
- "block",
- "contents",
- "flex",
- "flow-root",
- "flow-root list-item",
- "grid",
- "inline",
- "inline-block",
- "inline-flex",
- "inline-grid",
- "inline-table",
- "inline list-item",
- "inline flow-root list-item",
- "list-item",
- "none",
- "block ruby",
- "ruby",
- "ruby-base",
- "ruby-base-container",
- "ruby-text",
- "ruby-text-container",
- "table",
- "table-caption",
- "table-cell",
- "table-column",
- "table-column-group",
- "table-footer-group",
- "table-header-group",
- "table-row",
- "table-row-group",
- "-webkit-box",
- "-webkit-inline-box",
- ]);
- }
-}
-
-impl Debug for Display {
- fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
- f.debug_struct("Display")
- .field("List Item", &self.is_list_item())
- .field("Inside", &self.inside())
- .field("Outside", &self.outside())
- .finish()
- }
-}
-
-/// A specified value for the `contain-intrinsic-size` property.
-pub type ContainIntrinsicSize = GenericContainIntrinsicSize<NonNegativeLength>;
-
-/// A specified value for the `line-clamp` property.
-pub type LineClamp = GenericLineClamp<Integer>;
-
-/// A specified value for the `vertical-align` property.
-pub type VerticalAlign = GenericVerticalAlign<LengthPercentage>;
-
-impl Parse for VerticalAlign {
- fn parse<'i, 't>(
- context: &ParserContext,
- input: &mut Parser<'i, 't>,
- ) -> Result<Self, ParseError<'i>> {
- if let Ok(lp) =
- input.try_parse(|i| LengthPercentage::parse_quirky(context, i, AllowQuirks::Yes))
- {
- return Ok(GenericVerticalAlign::Length(lp));
- }
-
- Ok(GenericVerticalAlign::Keyword(VerticalAlignKeyword::parse(
- input,
- )?))
- }
-}
-
-/// A specified value for the `baseline-source` property.
-/// https://drafts.csswg.org/css-inline-3/#baseline-source
-#[derive(
- Clone,
- Copy,
- Debug,
- Eq,
- Hash,
- MallocSizeOf,
- Parse,
- PartialEq,
- SpecifiedValueInfo,
- ToCss,
- ToShmem,
- ToComputedValue,
- ToResolvedValue,
-)]
-#[repr(u8)]
-pub enum BaselineSource {
- /// `Last` for `inline-block`, `First` otherwise.
- Auto,
- /// Use first baseline for alignment.
- First,
- /// Use last baseline for alignment.
- Last,
-}
-
-/// https://drafts.csswg.org/css-scroll-snap-1/#snap-axis
-#[allow(missing_docs)]
-#[cfg_attr(feature = "servo", derive(Deserialize, Serialize))]
-#[derive(
- Clone,
- Copy,
- Debug,
- Eq,
- MallocSizeOf,
- Parse,
- PartialEq,
- SpecifiedValueInfo,
- ToComputedValue,
- ToCss,
- ToResolvedValue,
- ToShmem,
-)]
-#[repr(u8)]
-pub enum ScrollSnapAxis {
- X,
- Y,
- Block,
- Inline,
- Both,
-}
-
-/// https://drafts.csswg.org/css-scroll-snap-1/#snap-strictness
-#[allow(missing_docs)]
-#[cfg_attr(feature = "servo", derive(Deserialize, Serialize))]
-#[derive(
- Clone,
- Copy,
- Debug,
- Eq,
- MallocSizeOf,
- Parse,
- PartialEq,
- SpecifiedValueInfo,
- ToComputedValue,
- ToCss,
- ToResolvedValue,
- ToShmem,
-)]
-#[repr(u8)]
-pub enum ScrollSnapStrictness {
- #[css(skip)]
- None, // Used to represent scroll-snap-type: none. It's not parsed.
- Mandatory,
- Proximity,
-}
-
-/// https://drafts.csswg.org/css-scroll-snap-1/#scroll-snap-type
-#[allow(missing_docs)]
-#[cfg_attr(feature = "servo", derive(Deserialize, Serialize))]
-#[derive(
- Clone,
- Copy,
- Debug,
- Eq,
- MallocSizeOf,
- PartialEq,
- SpecifiedValueInfo,
- ToComputedValue,
- ToResolvedValue,
- ToShmem,
-)]
-#[repr(C)]
-pub struct ScrollSnapType {
- axis: ScrollSnapAxis,
- strictness: ScrollSnapStrictness,
-}
-
-impl ScrollSnapType {
- /// Returns `none`.
- #[inline]
- pub fn none() -> Self {
- Self {
- axis: ScrollSnapAxis::Both,
- strictness: ScrollSnapStrictness::None,
- }
- }
-}
-
-impl Parse for ScrollSnapType {
- /// none | [ x | y | block | inline | both ] [ mandatory | proximity ]?
- fn parse<'i, 't>(
- _context: &ParserContext,
- input: &mut Parser<'i, 't>,
- ) -> Result<Self, ParseError<'i>> {
- if input
- .try_parse(|input| input.expect_ident_matching("none"))
- .is_ok()
- {
- return Ok(ScrollSnapType::none());
- }
-
- let axis = ScrollSnapAxis::parse(input)?;
- let strictness = input
- .try_parse(ScrollSnapStrictness::parse)
- .unwrap_or(ScrollSnapStrictness::Proximity);
- Ok(Self { axis, strictness })
- }
-}
-
-impl ToCss for ScrollSnapType {
- fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
- where
- W: Write,
- {
- if self.strictness == ScrollSnapStrictness::None {
- return dest.write_str("none");
- }
- self.axis.to_css(dest)?;
- if self.strictness != ScrollSnapStrictness::Proximity {
- dest.write_char(' ')?;
- self.strictness.to_css(dest)?;
- }
- Ok(())
- }
-}
-
-/// Specified value of scroll-snap-align keyword value.
-#[allow(missing_docs)]
-#[derive(
- Clone,
- Copy,
- Debug,
- Eq,
- FromPrimitive,
- Hash,
- MallocSizeOf,
- Parse,
- PartialEq,
- SpecifiedValueInfo,
- ToComputedValue,
- ToCss,
- ToResolvedValue,
- ToShmem,
-)]
-#[repr(u8)]
-pub enum ScrollSnapAlignKeyword {
- None,
- Start,
- End,
- Center,
-}
-
-/// https://drafts.csswg.org/css-scroll-snap-1/#scroll-snap-align
-#[allow(missing_docs)]
-#[derive(
- Clone,
- Copy,
- Debug,
- Eq,
- MallocSizeOf,
- PartialEq,
- SpecifiedValueInfo,
- ToComputedValue,
- ToResolvedValue,
- ToShmem,
-)]
-#[repr(C)]
-pub struct ScrollSnapAlign {
- block: ScrollSnapAlignKeyword,
- inline: ScrollSnapAlignKeyword,
-}
-
-impl ScrollSnapAlign {
- /// Returns `none`.
- #[inline]
- pub fn none() -> Self {
- ScrollSnapAlign {
- block: ScrollSnapAlignKeyword::None,
- inline: ScrollSnapAlignKeyword::None,
- }
- }
-}
-
-impl Parse for ScrollSnapAlign {
- /// [ none | start | end | center ]{1,2}
- fn parse<'i, 't>(
- _context: &ParserContext,
- input: &mut Parser<'i, 't>,
- ) -> Result<ScrollSnapAlign, ParseError<'i>> {
- let block = ScrollSnapAlignKeyword::parse(input)?;
- let inline = input
- .try_parse(ScrollSnapAlignKeyword::parse)
- .unwrap_or(block);
- Ok(ScrollSnapAlign { block, inline })
- }
-}
-
-impl ToCss for ScrollSnapAlign {
- fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
- where
- W: Write,
- {
- self.block.to_css(dest)?;
- if self.block != self.inline {
- dest.write_char(' ')?;
- self.inline.to_css(dest)?;
- }
- Ok(())
- }
-}
-
-#[allow(missing_docs)]
-#[cfg_attr(feature = "servo", derive(Deserialize, Serialize))]
-#[derive(
- Clone,
- Copy,
- Debug,
- Eq,
- MallocSizeOf,
- Parse,
- PartialEq,
- SpecifiedValueInfo,
- ToComputedValue,
- ToCss,
- ToResolvedValue,
- ToShmem,
-)]
-#[repr(u8)]
-pub enum ScrollSnapStop {
- Normal,
- Always,
-}
-
-#[allow(missing_docs)]
-#[cfg_attr(feature = "servo", derive(Deserialize, Serialize))]
-#[derive(
- Clone,
- Copy,
- Debug,
- Eq,
- MallocSizeOf,
- Parse,
- PartialEq,
- SpecifiedValueInfo,
- ToComputedValue,
- ToCss,
- ToResolvedValue,
- ToShmem,
-)]
-#[repr(u8)]
-pub enum OverscrollBehavior {
- Auto,
- Contain,
- None,
-}
-
-#[allow(missing_docs)]
-#[cfg_attr(feature = "servo", derive(Deserialize, Serialize))]
-#[derive(
- Clone,
- Copy,
- Debug,
- Eq,
- MallocSizeOf,
- Parse,
- PartialEq,
- SpecifiedValueInfo,
- ToComputedValue,
- ToCss,
- ToResolvedValue,
- ToShmem,
-)]
-#[repr(u8)]
-pub enum OverflowAnchor {
- Auto,
- None,
-}
-
-#[allow(missing_docs)]
-#[cfg_attr(feature = "servo", derive(Deserialize, Serialize))]
-#[derive(
- Clone,
- Copy,
- Debug,
- Eq,
- MallocSizeOf,
- Parse,
- PartialEq,
- SpecifiedValueInfo,
- ToComputedValue,
- ToCss,
- ToResolvedValue,
- ToShmem,
-)]
-#[repr(u8)]
-pub enum OverflowClipBox {
- PaddingBox,
- ContentBox,
-}
-
-#[derive(
- Clone,
- Debug,
- Default,
- MallocSizeOf,
- PartialEq,
- SpecifiedValueInfo,
- ToComputedValue,
- ToCss,
- ToResolvedValue,
- ToShmem,
-)]
-#[css(comma)]
-#[repr(C)]
-/// Provides a rendering hint to the user agent, stating what kinds of changes
-/// the author expects to perform on the element.
-///
-/// `auto` is represented by an empty `features` list.
-///
-/// <https://drafts.csswg.org/css-will-change/#will-change>
-pub struct WillChange {
- /// The features that are supposed to change.
- ///
- /// TODO(emilio): Consider using ArcSlice since we just clone them from the
- /// specified value? That'd save an allocation, which could be worth it.
- #[css(iterable, if_empty = "auto")]
- features: crate::OwnedSlice<CustomIdent>,
- /// A bitfield with the kind of change that the value will create, based
- /// on the above field.
- #[css(skip)]
- bits: WillChangeBits,
-}
-
-impl WillChange {
- #[inline]
- /// Get default value of `will-change` as `auto`
- pub fn auto() -> Self {
- Self::default()
- }
-}
-
-bitflags! {
- /// The change bits that we care about.
- #[derive(Default, MallocSizeOf, SpecifiedValueInfo, ToComputedValue, ToResolvedValue, ToShmem)]
- #[repr(C)]
- pub struct WillChangeBits: u16 {
- /// Whether a property which can create a stacking context **on any
- /// box** will change.
- const STACKING_CONTEXT_UNCONDITIONAL = 1 << 0;
- /// Whether `transform` or related properties will change.
- const TRANSFORM = 1 << 1;
- /// Whether `scroll-position` will change.
- const SCROLL = 1 << 2;
- /// Whether `contain` will change.
- const CONTAIN = 1 << 3;
- /// Whether `opacity` will change.
- const OPACITY = 1 << 4;
- /// Whether `perspective` will change.
- const PERSPECTIVE = 1 << 5;
- /// Whether `z-index` will change.
- const Z_INDEX = 1 << 6;
- /// Whether any property which creates a containing block for non-svg
- /// text frames will change.
- const FIXPOS_CB_NON_SVG = 1 << 7;
- /// Whether the position property will change.
- const POSITION = 1 << 8;
- }
-}
-
-#[cfg(feature = "gecko")]
-fn change_bits_for_longhand(longhand: LonghandId) -> WillChangeBits {
- match longhand {
- LonghandId::Opacity => WillChangeBits::OPACITY,
- LonghandId::Contain => WillChangeBits::CONTAIN,
- LonghandId::Perspective => WillChangeBits::PERSPECTIVE,
- LonghandId::Position => {
- WillChangeBits::STACKING_CONTEXT_UNCONDITIONAL | WillChangeBits::POSITION
- },
- LonghandId::ZIndex => WillChangeBits::Z_INDEX,
- LonghandId::Transform |
- LonghandId::TransformStyle |
- LonghandId::Translate |
- LonghandId::Rotate |
- LonghandId::Scale |
- LonghandId::OffsetPath => WillChangeBits::TRANSFORM,
- LonghandId::BackdropFilter | LonghandId::Filter => {
- WillChangeBits::STACKING_CONTEXT_UNCONDITIONAL | WillChangeBits::FIXPOS_CB_NON_SVG
- },
- LonghandId::MixBlendMode |
- LonghandId::Isolation |
- LonghandId::MaskImage |
- LonghandId::ClipPath => WillChangeBits::STACKING_CONTEXT_UNCONDITIONAL,
- _ => WillChangeBits::empty(),
- }
-}
-
-#[cfg(feature = "gecko")]
-fn change_bits_for_maybe_property(ident: &str, context: &ParserContext) -> WillChangeBits {
- let id = match PropertyId::parse_ignoring_rule_type(ident, context) {
- Ok(id) => id,
- Err(..) => return WillChangeBits::empty(),
- };
-
- match id.as_shorthand() {
- Ok(shorthand) => shorthand
- .longhands()
- .fold(WillChangeBits::empty(), |flags, p| {
- flags | change_bits_for_longhand(p)
- }),
- Err(PropertyDeclarationId::Longhand(longhand)) => change_bits_for_longhand(longhand),
- Err(PropertyDeclarationId::Custom(..)) => WillChangeBits::empty(),
- }
-}
-
-#[cfg(feature = "gecko")]
-impl Parse for WillChange {
- /// auto | <animateable-feature>#
- fn parse<'i, 't>(
- context: &ParserContext,
- input: &mut Parser<'i, 't>,
- ) -> Result<Self, ParseError<'i>> {
- if input
- .try_parse(|input| input.expect_ident_matching("auto"))
- .is_ok()
- {
- return Ok(Self::default());
- }
-
- let mut bits = WillChangeBits::empty();
- let custom_idents = input.parse_comma_separated(|i| {
- let location = i.current_source_location();
- let parser_ident = i.expect_ident()?;
- let ident = CustomIdent::from_ident(
- location,
- parser_ident,
- &["will-change", "none", "all", "auto"],
- )?;
-
- if context.in_ua_sheet() && ident.0 == atom!("-moz-fixed-pos-containing-block") {
- bits |= WillChangeBits::FIXPOS_CB_NON_SVG;
- } else if ident.0 == atom!("scroll-position") {
- bits |= WillChangeBits::SCROLL;
- } else {
- bits |= change_bits_for_maybe_property(&parser_ident, context);
- }
- Ok(ident)
- })?;
-
- Ok(Self {
- features: custom_idents.into(),
- bits,
- })
- }
-}
-
-bitflags! {
- /// Values for the `touch-action` property.
- #[derive(MallocSizeOf, SpecifiedValueInfo, ToComputedValue, ToCss, ToResolvedValue, ToShmem, Parse)]
- #[css(bitflags(single = "none,auto,manipulation", mixed = "pan-x,pan-y,pinch-zoom"))]
- #[repr(C)]
- pub struct TouchAction: u8 {
- /// `none` variant
- const NONE = 1 << 0;
- /// `auto` variant
- const AUTO = 1 << 1;
- /// `pan-x` variant
- const PAN_X = 1 << 2;
- /// `pan-y` variant
- const PAN_Y = 1 << 3;
- /// `manipulation` variant
- const MANIPULATION = 1 << 4;
- /// `pinch-zoom` variant
- const PINCH_ZOOM = 1 << 5;
- }
-}
-
-impl TouchAction {
- #[inline]
- /// Get default `touch-action` as `auto`
- pub fn auto() -> TouchAction {
- TouchAction::AUTO
- }
-}
-
-bitflags! {
- #[derive(MallocSizeOf, Parse, SpecifiedValueInfo, ToComputedValue, ToCss, ToResolvedValue, ToShmem)]
- #[css(bitflags(single = "none,strict,content", mixed="size,layout,style,paint,inline-size", overlapping_bits))]
- #[repr(C)]
- /// Constants for contain: https://drafts.csswg.org/css-contain/#contain-property
- pub struct Contain: u8 {
- /// `none` variant, just for convenience.
- const NONE = 0;
- /// `inline-size` variant, turns on single-axis inline size containment
- const INLINE_SIZE = 1 << 0;
- /// `block-size` variant, turns on single-axis block size containment, internal only
- const BLOCK_SIZE = 1 << 1;
- /// `layout` variant, turns on layout containment
- const LAYOUT = 1 << 2;
- /// `style` variant, turns on style containment
- const STYLE = 1 << 3;
- /// `paint` variant, turns on paint containment
- const PAINT = 1 << 4;
- /// 'size' variant, turns on size containment
- const SIZE = 1 << 5 | Contain::INLINE_SIZE.bits | Contain::BLOCK_SIZE.bits;
- /// `content` variant, turns on layout and paint containment
- const CONTENT = 1 << 6 | Contain::LAYOUT.bits | Contain::STYLE.bits | Contain::PAINT.bits;
- /// `strict` variant, turns on all types of containment
- const STRICT = 1 << 7 | Contain::LAYOUT.bits | Contain::STYLE.bits | Contain::PAINT.bits | Contain::SIZE.bits;
- }
-}
-
-impl Parse for ContainIntrinsicSize {
- /// none | <length> | auto <length>
- fn parse<'i, 't>(
- context: &ParserContext,
- input: &mut Parser<'i, 't>,
- ) -> Result<Self, ParseError<'i>> {
- if let Ok(l) = input.try_parse(|i| NonNegativeLength::parse(context, i)) {
- return Ok(Self::Length(l));
- }
-
- if input.try_parse(|i| i.expect_ident_matching("auto")).is_ok() {
- let l = NonNegativeLength::parse(context, input)?;
- return Ok(Self::AutoLength(l));
- }
-
- input.expect_ident_matching("none")?;
- Ok(Self::None)
- }
-}
-
-impl Parse for LineClamp {
- /// none | <positive-integer>
- fn parse<'i, 't>(
- context: &ParserContext,
- input: &mut Parser<'i, 't>,
- ) -> Result<Self, ParseError<'i>> {
- if let Ok(i) =
- input.try_parse(|i| crate::values::specified::PositiveInteger::parse(context, i))
- {
- return Ok(Self(i.0));
- }
- input.expect_ident_matching("none")?;
- Ok(Self::none())
- }
-}
-
-/// https://drafts.csswg.org/css-contain-2/#content-visibility
-#[cfg_attr(feature = "servo", derive(Deserialize, Serialize))]
-#[derive(
- Clone,
- Copy,
- Debug,
- Eq,
- MallocSizeOf,
- Parse,
- PartialEq,
- SpecifiedValueInfo,
- ToComputedValue,
- ToCss,
- ToResolvedValue,
- ToShmem,
-)]
-#[repr(u8)]
-pub enum ContentVisibility {
- /// `auto` variant, the element turns on layout containment, style containment, and paint
- /// containment. In addition, if the element is not relevant to the user (such as by being
- /// offscreen) it also skips its content
- Auto,
- /// `hidden` variant, the element skips its content
- Hidden,
- /// 'visible' variant, no effect
- Visible,
-}
-
-#[derive(
- Clone,
- Copy,
- Debug,
- PartialEq,
- Eq,
- MallocSizeOf,
- SpecifiedValueInfo,
- ToComputedValue,
- ToCss,
- Parse,
- ToResolvedValue,
- ToShmem,
-)]
-#[repr(u8)]
-#[allow(missing_docs)]
-/// https://drafts.csswg.org/css-contain-3/#container-type
-pub enum ContainerType {
- /// The `normal` variant.
- Normal,
- /// The `inline-size` variant.
- InlineSize,
- /// The `size` variant.
- Size,
-}
-
-impl ContainerType {
- /// Is this container-type: normal?
- pub fn is_normal(self) -> bool {
- self == Self::Normal
- }
-
- /// Is this type containing size in any way?
- pub fn is_size_container_type(self) -> bool {
- !self.is_normal()
- }
-}
-
-/// https://drafts.csswg.org/css-contain-3/#container-name
-#[repr(transparent)]
-#[derive(
- Clone,
- Debug,
- MallocSizeOf,
- PartialEq,
- SpecifiedValueInfo,
- ToComputedValue,
- ToCss,
- ToResolvedValue,
- ToShmem,
-)]
-pub struct ContainerName(#[css(iterable, if_empty = "none")] pub crate::OwnedSlice<CustomIdent>);
-
-impl ContainerName {
- /// Return the `none` value.
- pub fn none() -> Self {
- Self(Default::default())
- }
-
- /// Returns whether this is the `none` value.
- pub fn is_none(&self) -> bool {
- self.0.is_empty()
- }
-
- fn parse_internal<'i>(
- input: &mut Parser<'i, '_>,
- for_query: bool,
- ) -> Result<Self, ParseError<'i>> {
- let mut idents = vec![];
- let location = input.current_source_location();
- let first = input.expect_ident()?;
- if !for_query && first.eq_ignore_ascii_case("none") {
- return Ok(Self::none());
- }
- const DISALLOWED_CONTAINER_NAMES: &'static [&'static str] = &["none", "not", "or", "and"];
- idents.push(CustomIdent::from_ident(
- location,
- first,
- DISALLOWED_CONTAINER_NAMES,
- )?);
- if !for_query {
- while let Ok(name) = input.try_parse(|input| {
- let ident = input.expect_ident()?;
- CustomIdent::from_ident(location, &ident, DISALLOWED_CONTAINER_NAMES)
- }) {
- idents.push(name);
- }
- }
- Ok(ContainerName(idents.into()))
- }
-
- /// https://github.com/w3c/csswg-drafts/issues/7203
- /// Only a single name allowed in @container rule.
- /// Disallow none for container-name in @container rule.
- pub fn parse_for_query<'i, 't>(
- _: &ParserContext,
- input: &mut Parser<'i, 't>,
- ) -> Result<Self, ParseError<'i>> {
- Self::parse_internal(input, /* for_query = */ true)
- }
-}
-
-impl Parse for ContainerName {
- fn parse<'i, 't>(
- _: &ParserContext,
- input: &mut Parser<'i, 't>,
- ) -> Result<Self, ParseError<'i>> {
- Self::parse_internal(input, /* for_query = */ false)
- }
-}
-
-/// A specified value for the `perspective` property.
-pub type Perspective = GenericPerspective<NonNegativeLength>;
-
-#[allow(missing_docs)]
-#[cfg_attr(feature = "servo", derive(Deserialize, Serialize))]
-#[derive(
- Clone, Copy, Debug, Eq, Hash, MallocSizeOf, Parse, PartialEq, SpecifiedValueInfo, ToCss, ToShmem,
-)]
-/// https://drafts.csswg.org/css-box/#propdef-float
-pub enum Float {
- Left,
- Right,
- None,
- // https://drafts.csswg.org/css-logical-props/#float-clear
- InlineStart,
- InlineEnd,
-}
-
-#[allow(missing_docs)]
-#[cfg_attr(feature = "servo", derive(Deserialize, Serialize))]
-#[derive(
- Clone, Copy, Debug, Eq, Hash, MallocSizeOf, Parse, PartialEq, SpecifiedValueInfo, ToCss, ToShmem,
-)]
-/// https://drafts.csswg.org/css2/#propdef-clear
-pub enum Clear {
- None,
- Left,
- Right,
- Both,
- // https://drafts.csswg.org/css-logical-props/#float-clear
- InlineStart,
- InlineEnd,
-}
-
-/// https://drafts.csswg.org/css-ui/#propdef-resize
-#[allow(missing_docs)]
-#[cfg_attr(feature = "servo", derive(Deserialize, Serialize))]
-#[derive(
- Clone, Copy, Debug, Eq, Hash, MallocSizeOf, Parse, PartialEq, SpecifiedValueInfo, ToCss, ToShmem,
-)]
-pub enum Resize {
- None,
- Both,
- Horizontal,
- Vertical,
- // https://drafts.csswg.org/css-logical-1/#resize
- Inline,
- Block,
-}
-
-/// The value for the `appearance` property.
-///
-/// https://developer.mozilla.org/en-US/docs/Web/CSS/-moz-appearance
-#[allow(missing_docs)]
-#[derive(
- Clone,
- Copy,
- Debug,
- Eq,
- Hash,
- MallocSizeOf,
- Parse,
- PartialEq,
- SpecifiedValueInfo,
- ToCss,
- ToComputedValue,
- ToResolvedValue,
- ToShmem,
-)]
-#[repr(u8)]
-pub enum Appearance {
- /// No appearance at all.
- None,
- /// Default appearance for the element.
- ///
- /// This value doesn't make sense for -moz-default-appearance, but we don't bother to guard
- /// against parsing it.
- Auto,
- /// A searchfield.
- Searchfield,
- /// A multi-line text field, e.g. HTML <textarea>.
- Textarea,
- /// A checkbox element.
- Checkbox,
- /// A radio element within a radio group.
- Radio,
- /// A dropdown list.
- Menulist,
- /// List boxes.
- Listbox,
- /// A horizontal meter bar.
- Meter,
- /// A horizontal progress bar.
- ProgressBar,
- /// A typical dialog button.
- Button,
- /// A single-line text field, e.g. HTML <input type=text>.
- Textfield,
- /// The dropdown button(s) that open up a dropdown list.
- MenulistButton,
- /// Various arrows that go in buttons
- #[parse(condition = "ParserContext::in_ua_or_chrome_sheet")]
- ButtonArrowDown,
- #[parse(condition = "ParserContext::in_ua_or_chrome_sheet")]
- ButtonArrowNext,
- #[parse(condition = "ParserContext::in_ua_or_chrome_sheet")]
- ButtonArrowPrevious,
- #[parse(condition = "ParserContext::in_ua_or_chrome_sheet")]
- ButtonArrowUp,
- /// A dual toolbar button (e.g., a Back button with a dropdown)
- #[parse(condition = "ParserContext::in_ua_or_chrome_sheet")]
- Dualbutton,
- /// A groupbox.
- #[parse(condition = "ParserContext::in_ua_or_chrome_sheet")]
- Groupbox,
- /// Menu Bar background
- #[parse(condition = "ParserContext::in_ua_or_chrome_sheet")]
- Menubar,
- /// <menu> and <menuitem> appearances
- #[parse(condition = "ParserContext::in_ua_or_chrome_sheet")]
- Menuitem,
- #[parse(condition = "ParserContext::in_ua_or_chrome_sheet")]
- Checkmenuitem,
- #[parse(condition = "ParserContext::in_ua_or_chrome_sheet")]
- Radiomenuitem,
- /// For text on non-iconic menuitems only
- #[parse(condition = "ParserContext::in_ua_or_chrome_sheet")]
- Menuitemtext,
- /// The text part of a dropdown list, to left of button.
- #[parse(condition = "ParserContext::in_ua_or_chrome_sheet")]
- MenulistText,
- /// Menu Popup background.
- #[parse(condition = "ParserContext::in_ua_or_chrome_sheet")]
- Menupopup,
- /// menu checkbox/radio appearances
- #[parse(condition = "ParserContext::in_ua_or_chrome_sheet")]
- Menucheckbox,
- #[parse(condition = "ParserContext::in_ua_or_chrome_sheet")]
- Menuradio,
- #[parse(condition = "ParserContext::in_ua_or_chrome_sheet")]
- Menuseparator,
- #[parse(condition = "ParserContext::in_ua_or_chrome_sheet")]
- Menuarrow,
- /// An image in the menu gutter, like in bookmarks or history.
- #[parse(condition = "ParserContext::in_ua_or_chrome_sheet")]
- Menuimage,
- /// The meter bar's meter indicator.
- #[parse(condition = "ParserContext::in_ua_or_chrome_sheet")]
- Meterchunk,
- /// The "arrowed" part of the dropdown button that open up a dropdown list.
- #[parse(condition = "ParserContext::in_ua_or_chrome_sheet")]
- MozMenulistArrowButton,
- /// For HTML's <input type=number>
- #[parse(condition = "ParserContext::in_ua_or_chrome_sheet")]
- NumberInput,
- /// The progress bar's progress indicator
- #[parse(condition = "ParserContext::in_ua_or_chrome_sheet")]
- Progresschunk,
- /// A generic container that always repaints on state changes. This is a
- /// hack to make XUL checkboxes and radio buttons work.
- #[parse(condition = "ParserContext::in_ua_or_chrome_sheet")]
- CheckboxContainer,
- #[parse(condition = "ParserContext::in_ua_or_chrome_sheet")]
- RadioContainer,
- /// The label part of a checkbox or radio button, used for painting a focus
- /// outline.
- #[parse(condition = "ParserContext::in_ua_or_chrome_sheet")]
- CheckboxLabel,
- #[parse(condition = "ParserContext::in_ua_or_chrome_sheet")]
- RadioLabel,
- /// nsRangeFrame and its subparts
- #[parse(condition = "ParserContext::in_ua_or_chrome_sheet")]
- Range,
- #[parse(condition = "ParserContext::in_ua_or_chrome_sheet")]
- RangeThumb,
- /// The scrollbar slider
- #[parse(condition = "ParserContext::in_ua_or_chrome_sheet")]
- ScrollbarHorizontal,
- #[parse(condition = "ParserContext::in_ua_or_chrome_sheet")]
- ScrollbarVertical,
- /// A scrollbar button (up/down/left/right).
- /// Keep these in order (some code casts these values to `int` in order to
- /// compare them against each other).
- #[parse(condition = "ParserContext::in_ua_or_chrome_sheet")]
- ScrollbarbuttonUp,
- #[parse(condition = "ParserContext::in_ua_or_chrome_sheet")]
- ScrollbarbuttonDown,
- #[parse(condition = "ParserContext::in_ua_or_chrome_sheet")]
- ScrollbarbuttonLeft,
- #[parse(condition = "ParserContext::in_ua_or_chrome_sheet")]
- ScrollbarbuttonRight,
- /// The scrollbar thumb.
- #[parse(condition = "ParserContext::in_ua_or_chrome_sheet")]
- ScrollbarthumbHorizontal,
- #[parse(condition = "ParserContext::in_ua_or_chrome_sheet")]
- ScrollbarthumbVertical,
- /// The scrollbar track.
- #[parse(condition = "ParserContext::in_ua_or_chrome_sheet")]
- ScrollbartrackHorizontal,
- #[parse(condition = "ParserContext::in_ua_or_chrome_sheet")]
- ScrollbartrackVertical,
- /// The scroll corner
- #[parse(condition = "ParserContext::in_ua_or_chrome_sheet")]
- Scrollcorner,
- /// A separator. Can be horizontal or vertical.
- #[parse(condition = "ParserContext::in_ua_or_chrome_sheet")]
- Separator,
- /// A spin control (up/down control for time/date pickers).
- #[parse(condition = "ParserContext::in_ua_or_chrome_sheet")]
- Spinner,
- /// The up button of a spin control.
- #[parse(condition = "ParserContext::in_ua_or_chrome_sheet")]
- SpinnerUpbutton,
- /// The down button of a spin control.
- #[parse(condition = "ParserContext::in_ua_or_chrome_sheet")]
- SpinnerDownbutton,
- /// The textfield of a spin control
- #[parse(condition = "ParserContext::in_ua_or_chrome_sheet")]
- SpinnerTextfield,
- /// A splitter. Can be horizontal or vertical.
- #[parse(condition = "ParserContext::in_ua_or_chrome_sheet")]
- Splitter,
- /// A status bar in a main application window.
- #[parse(condition = "ParserContext::in_ua_or_chrome_sheet")]
- Statusbar,
- /// A single tab in a tab widget.
- #[parse(condition = "ParserContext::in_ua_or_chrome_sheet")]
- Tab,
- /// A single pane (inside the tabpanels container).
- #[parse(condition = "ParserContext::in_ua_or_chrome_sheet")]
- Tabpanel,
- /// The tab panels container.
- #[parse(condition = "ParserContext::in_ua_or_chrome_sheet")]
- Tabpanels,
- /// The tabs scroll arrows (left/right).
- #[parse(condition = "ParserContext::in_ua_or_chrome_sheet")]
- TabScrollArrowBack,
- #[parse(condition = "ParserContext::in_ua_or_chrome_sheet")]
- TabScrollArrowForward,
- /// A toolbar in an application window.
- #[parse(condition = "ParserContext::in_ua_or_chrome_sheet")]
- Toolbar,
- /// A single toolbar button (with no associated dropdown).
- #[parse(condition = "ParserContext::in_ua_or_chrome_sheet")]
- Toolbarbutton,
- /// The dropdown portion of a toolbar button
- #[parse(condition = "ParserContext::in_ua_or_chrome_sheet")]
- ToolbarbuttonDropdown,
- /// The gripper for a toolbar.
- #[parse(condition = "ParserContext::in_ua_or_chrome_sheet")]
- Toolbargripper,
- /// The toolbox that contains the toolbars.
- #[parse(condition = "ParserContext::in_ua_or_chrome_sheet")]
- Toolbox,
- /// A tooltip.
- #[parse(condition = "ParserContext::in_ua_or_chrome_sheet")]
- Tooltip,
- /// A listbox or tree widget header
- #[parse(condition = "ParserContext::in_ua_or_chrome_sheet")]
- Treeheader,
- /// An individual header cell
- #[parse(condition = "ParserContext::in_ua_or_chrome_sheet")]
- Treeheadercell,
- /// The sort arrow for a header.
- #[parse(condition = "ParserContext::in_ua_or_chrome_sheet")]
- Treeheadersortarrow,
- /// A tree item.
- #[parse(condition = "ParserContext::in_ua_or_chrome_sheet")]
- Treeitem,
- /// A tree widget branch line
- #[parse(condition = "ParserContext::in_ua_or_chrome_sheet")]
- Treeline,
- /// A tree widget twisty.
- #[parse(condition = "ParserContext::in_ua_or_chrome_sheet")]
- Treetwisty,
- /// Open tree widget twisty.
- #[parse(condition = "ParserContext::in_ua_or_chrome_sheet")]
- Treetwistyopen,
- /// A tree widget.
- #[parse(condition = "ParserContext::in_ua_or_chrome_sheet")]
- Treeview,
- /// Window and dialog backgrounds.
- #[parse(condition = "ParserContext::in_ua_or_chrome_sheet")]
- Window,
- #[parse(condition = "ParserContext::in_ua_or_chrome_sheet")]
- Dialog,
-
- /// Vista Rebars.
- #[parse(condition = "ParserContext::in_ua_or_chrome_sheet")]
- MozWinCommunicationsToolbox,
- #[parse(condition = "ParserContext::in_ua_or_chrome_sheet")]
- MozWinMediaToolbox,
- #[parse(condition = "ParserContext::in_ua_or_chrome_sheet")]
- MozWinBrowsertabbarToolbox,
- #[parse(condition = "ParserContext::in_ua_or_chrome_sheet")]
- MozWinBorderlessGlass,
- /// -moz-apperance style used in setting proper glass margins.
- #[parse(condition = "ParserContext::in_ua_or_chrome_sheet")]
- MozWinExcludeGlass,
-
- /// Mac help button.
- #[parse(condition = "ParserContext::in_ua_or_chrome_sheet")]
- MozMacHelpButton,
-
- /// Windows themed window frame elements.
- #[parse(condition = "ParserContext::in_ua_or_chrome_sheet")]
- MozWindowButtonBox,
- #[parse(condition = "ParserContext::in_ua_or_chrome_sheet")]
- MozWindowButtonBoxMaximized,
- #[parse(condition = "ParserContext::in_ua_or_chrome_sheet")]
- MozWindowButtonClose,
- #[parse(condition = "ParserContext::in_ua_or_chrome_sheet")]
- MozWindowButtonMaximize,
- #[parse(condition = "ParserContext::in_ua_or_chrome_sheet")]
- MozWindowButtonMinimize,
- #[parse(condition = "ParserContext::in_ua_or_chrome_sheet")]
- MozWindowButtonRestore,
- #[parse(condition = "ParserContext::in_ua_or_chrome_sheet")]
- MozWindowTitlebar,
- #[parse(condition = "ParserContext::in_ua_or_chrome_sheet")]
- MozWindowTitlebarMaximized,
-
- #[parse(condition = "ParserContext::in_ua_or_chrome_sheet")]
- MozMacActiveSourceListSelection,
- #[parse(condition = "ParserContext::in_ua_or_chrome_sheet")]
- MozMacDisclosureButtonClosed,
- #[parse(condition = "ParserContext::in_ua_or_chrome_sheet")]
- MozMacDisclosureButtonOpen,
- #[parse(condition = "ParserContext::in_ua_or_chrome_sheet")]
- MozMacSourceList,
- #[parse(condition = "ParserContext::in_ua_or_chrome_sheet")]
- MozMacSourceListSelection,
-
- /// A themed focus outline (for outline:auto).
- ///
- /// This isn't exposed to CSS at all, just here for convenience.
- #[css(skip)]
- FocusOutline,
-
- /// A dummy variant that should be last to let the GTK widget do hackery.
- #[css(skip)]
- Count,
-}
-
-/// A kind of break between two boxes.
-///
-/// https://drafts.csswg.org/css-break/#break-between
-#[allow(missing_docs)]
-#[derive(
- Clone,
- Copy,
- Debug,
- Eq,
- Hash,
- MallocSizeOf,
- Parse,
- PartialEq,
- SpecifiedValueInfo,
- ToCss,
- ToComputedValue,
- ToResolvedValue,
- ToShmem,
-)]
-#[repr(u8)]
-pub enum BreakBetween {
- Always,
- Auto,
- Page,
- Avoid,
- Left,
- Right,
-}
-
-impl BreakBetween {
- /// Parse a legacy break-between value for `page-break-{before,after}`.
- ///
- /// See https://drafts.csswg.org/css-break/#page-break-properties.
- #[cfg(feature = "gecko")]
- #[inline]
- pub(crate) fn parse_legacy<'i>(
- _: &ParserContext,
- input: &mut Parser<'i, '_>,
- ) -> Result<Self, ParseError<'i>> {
- let break_value = BreakBetween::parse(input)?;
- match break_value {
- BreakBetween::Always => Ok(BreakBetween::Page),
- BreakBetween::Auto | BreakBetween::Avoid | BreakBetween::Left | BreakBetween::Right => {
- Ok(break_value)
- },
- BreakBetween::Page => {
- Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError))
- },
- }
- }
-
- /// Serialize a legacy break-between value for `page-break-*`.
- ///
- /// See https://drafts.csswg.org/css-break/#page-break-properties.
- #[cfg(feature = "gecko")]
- pub(crate) fn to_css_legacy<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
- where
- W: Write,
- {
- match *self {
- BreakBetween::Auto | BreakBetween::Avoid | BreakBetween::Left | BreakBetween::Right => {
- self.to_css(dest)
- },
- BreakBetween::Page => dest.write_str("always"),
- BreakBetween::Always => Ok(()),
- }
- }
-}
-
-/// A kind of break within a box.
-///
-/// https://drafts.csswg.org/css-break/#break-within
-#[allow(missing_docs)]
-#[derive(
- Clone,
- Copy,
- Debug,
- Eq,
- Hash,
- MallocSizeOf,
- Parse,
- PartialEq,
- SpecifiedValueInfo,
- ToCss,
- ToComputedValue,
- ToResolvedValue,
- ToShmem,
-)]
-#[repr(u8)]
-pub enum BreakWithin {
- Auto,
- Avoid,
- AvoidPage,
- AvoidColumn,
-}
-
-impl BreakWithin {
- /// Parse a legacy break-between value for `page-break-inside`.
- ///
- /// See https://drafts.csswg.org/css-break/#page-break-properties.
- #[cfg(feature = "gecko")]
- #[inline]
- pub(crate) fn parse_legacy<'i>(
- _: &ParserContext,
- input: &mut Parser<'i, '_>,
- ) -> Result<Self, ParseError<'i>> {
- let break_value = BreakWithin::parse(input)?;
- match break_value {
- BreakWithin::Auto | BreakWithin::Avoid => Ok(break_value),
- BreakWithin::AvoidPage | BreakWithin::AvoidColumn => {
- Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError))
- },
- }
- }
-
- /// Serialize a legacy break-between value for `page-break-inside`.
- ///
- /// See https://drafts.csswg.org/css-break/#page-break-properties.
- #[cfg(feature = "gecko")]
- pub(crate) fn to_css_legacy<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
- where
- W: Write,
- {
- match *self {
- BreakWithin::Auto | BreakWithin::Avoid => self.to_css(dest),
- BreakWithin::AvoidPage | BreakWithin::AvoidColumn => Ok(()),
- }
- }
-}
-
-/// The value for the `overflow-x` / `overflow-y` properties.
-#[allow(missing_docs)]
-#[derive(
- Clone,
- Copy,
- Debug,
- Eq,
- Hash,
- MallocSizeOf,
- PartialEq,
- SpecifiedValueInfo,
- ToCss,
- ToComputedValue,
- ToResolvedValue,
- ToShmem,
-)]
-#[repr(u8)]
-pub enum Overflow {
- Visible,
- Hidden,
- Scroll,
- Auto,
- #[cfg(feature = "gecko")]
- Clip,
-}
-
-// This can be derived once we remove or keep `-moz-hidden-unscrollable`
-// indefinitely.
-impl Parse for Overflow {
- fn parse<'i, 't>(
- _: &ParserContext,
- input: &mut Parser<'i, 't>,
- ) -> Result<Self, ParseError<'i>> {
- Ok(try_match_ident_ignore_ascii_case! { input,
- "visible" => Self::Visible,
- "hidden" => Self::Hidden,
- "scroll" => Self::Scroll,
- "auto" => Self::Auto,
- #[cfg(feature = "gecko")]
- "clip" => Self::Clip,
- #[cfg(feature = "gecko")]
- "-moz-hidden-unscrollable" if static_prefs::pref!("layout.css.overflow-moz-hidden-unscrollable.enabled") => {
- Overflow::Clip
- },
- "overlay" if static_prefs::pref!("layout.css.overflow-overlay.enabled") => {
- Overflow::Auto
- },
- })
- }
-}
-
-impl Overflow {
- /// Return true if the value will create a scrollable box.
- #[inline]
- pub fn is_scrollable(&self) -> bool {
- matches!(*self, Self::Hidden | Self::Scroll | Self::Auto)
- }
- /// Convert the value to a scrollable value if it's not already scrollable.
- /// This maps `visible` to `auto` and `clip` to `hidden`.
- #[inline]
- pub fn to_scrollable(&self) -> Self {
- match *self {
- Self::Hidden | Self::Scroll | Self::Auto => *self,
- Self::Visible => Self::Auto,
- #[cfg(feature = "gecko")]
- Self::Clip => Self::Hidden,
- }
- }
-}
-
-bitflags! {
- #[derive(MallocSizeOf, SpecifiedValueInfo, ToCss, ToComputedValue, ToResolvedValue, ToShmem, Parse)]
- #[repr(C)]
- #[css(bitflags(single = "auto", mixed = "stable,both-edges", validate_mixed="Self::has_stable"))]
- /// Values for scrollbar-gutter:
- /// <https://drafts.csswg.org/css-overflow-3/#scrollbar-gutter-property>
- pub struct ScrollbarGutter: u8 {
- /// `auto` variant. Just for convenience if there is no flag set.
- const AUTO = 0;
- /// `stable` variant.
- const STABLE = 1 << 0;
- /// `both-edges` variant.
- const BOTH_EDGES = 1 << 1;
- }
-}
-
-impl ScrollbarGutter {
- #[inline]
- fn has_stable(&self) -> bool {
- self.intersects(Self::STABLE)
- }
-}
diff --git a/components/style/values/specified/calc.rs b/components/style/values/specified/calc.rs
deleted file mode 100644
index d4cc64c2823..00000000000
--- a/components/style/values/specified/calc.rs
+++ /dev/null
@@ -1,1047 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
-
-//! [Calc expressions][calc].
-//!
-//! [calc]: https://drafts.csswg.org/css-values/#calc-notation
-
-use crate::parser::ParserContext;
-use crate::values::generics::calc as generic;
-use crate::values::generics::calc::{MinMaxOp, ModRemOp, RoundingStrategy, SortKey};
-use crate::values::specified::length::{AbsoluteLength, FontRelativeLength, NoCalcLength};
-use crate::values::specified::length::{ContainerRelativeLength, ViewportPercentageLength};
-use crate::values::specified::{self, Angle, Resolution, Time};
-use crate::values::{serialize_number, serialize_percentage, CSSFloat, CSSInteger};
-use cssparser::{AngleOrNumber, CowRcStr, NumberOrPercentage, Parser, Token};
-use smallvec::SmallVec;
-use std::cmp;
-use std::fmt::{self, Write};
-use style_traits::values::specified::AllowedNumericType;
-use style_traits::{CssWriter, ParseError, SpecifiedValueInfo, StyleParseErrorKind, ToCss};
-
-fn trig_enabled() -> bool {
- static_prefs::pref!("layout.css.trig.enabled")
-}
-
-fn nan_inf_enabled() -> bool {
- static_prefs::pref!("layout.css.nan-inf.enabled")
-}
-
-/// The name of the mathematical function that we're parsing.
-#[derive(Clone, Copy, Debug, Parse)]
-pub enum MathFunction {
- /// `calc()`: https://drafts.csswg.org/css-values-4/#funcdef-calc
- Calc,
- /// `min()`: https://drafts.csswg.org/css-values-4/#funcdef-min
- Min,
- /// `max()`: https://drafts.csswg.org/css-values-4/#funcdef-max
- Max,
- /// `clamp()`: https://drafts.csswg.org/css-values-4/#funcdef-clamp
- Clamp,
- /// `round()`: https://drafts.csswg.org/css-values-4/#funcdef-round
- Round,
- /// `mod()`: https://drafts.csswg.org/css-values-4/#funcdef-mod
- Mod,
- /// `rem()`: https://drafts.csswg.org/css-values-4/#funcdef-rem
- Rem,
- /// `sin()`: https://drafts.csswg.org/css-values-4/#funcdef-sin
- Sin,
- /// `cos()`: https://drafts.csswg.org/css-values-4/#funcdef-cos
- Cos,
- /// `tan()`: https://drafts.csswg.org/css-values-4/#funcdef-tan
- Tan,
- /// `asin()`: https://drafts.csswg.org/css-values-4/#funcdef-asin
- Asin,
- /// `acos()`: https://drafts.csswg.org/css-values-4/#funcdef-acos
- Acos,
- /// `atan()`: https://drafts.csswg.org/css-values-4/#funcdef-atan
- Atan,
- /// `atan2()`: https://drafts.csswg.org/css-values-4/#funcdef-atan2
- Atan2,
- /// `pow()`: https://drafts.csswg.org/css-values-4/#funcdef-pow
- Pow,
- /// `sqrt()`: https://drafts.csswg.org/css-values-4/#funcdef-sqrt
- Sqrt,
- /// `hypot()`: https://drafts.csswg.org/css-values-4/#funcdef-hypot
- Hypot,
- /// `log()`: https://drafts.csswg.org/css-values-4/#funcdef-log
- Log,
- /// `exp()`: https://drafts.csswg.org/css-values-4/#funcdef-exp
- Exp,
-}
-
-/// A leaf node inside a `Calc` expression's AST.
-#[derive(Clone, Debug, MallocSizeOf, PartialEq, ToShmem)]
-pub enum Leaf {
- /// `<length>`
- Length(NoCalcLength),
- /// `<angle>`
- Angle(Angle),
- /// `<time>`
- Time(Time),
- /// `<resolution>`
- Resolution(Resolution),
- /// `<percentage>`
- Percentage(CSSFloat),
- /// `<number>`
- Number(CSSFloat),
-}
-
-impl Leaf {
- fn as_length(&self) -> Option<&NoCalcLength> {
- match *self {
- Self::Length(ref l) => Some(l),
- _ => None,
- }
- }
-}
-
-impl ToCss for Leaf {
- fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
- where
- W: Write,
- {
- match *self {
- Self::Length(ref l) => l.to_css(dest),
- Self::Number(n) => serialize_number(n, /* was_calc = */ false, dest),
- Self::Resolution(ref r) => r.to_css(dest),
- Self::Percentage(p) => serialize_percentage(p, dest),
- Self::Angle(ref a) => a.to_css(dest),
- Self::Time(ref t) => t.to_css(dest),
- }
- }
-}
-
-bitflags! {
- /// Expected units we allow parsing within a `calc()` expression.
- ///
- /// This is used as a hint for the parser to fast-reject invalid
- /// expressions. Numbers are always allowed because they multiply other
- /// units.
- struct CalcUnits: u8 {
- const LENGTH = 1 << 0;
- const PERCENTAGE = 1 << 1;
- const ANGLE = 1 << 2;
- const TIME = 1 << 3;
- const RESOLUTION = 1 << 3;
-
- const LENGTH_PERCENTAGE = Self::LENGTH.bits | Self::PERCENTAGE.bits;
- // NOTE: When you add to this, make sure to make Atan2 deal with these.
- const ALL = Self::LENGTH.bits | Self::PERCENTAGE.bits | Self::ANGLE.bits | Self::TIME.bits | Self::RESOLUTION.bits;
- }
-}
-
-/// A struct to hold a simplified `<length>` or `<percentage>` expression.
-///
-/// In some cases, e.g. DOMMatrix, we support calc(), but reject all the
-/// relative lengths, and to_computed_pixel_length_without_context() handles
-/// this case. Therefore, if you want to add a new field, please make sure this
-/// function work properly.
-#[derive(Clone, Debug, MallocSizeOf, PartialEq, ToCss, ToShmem)]
-#[allow(missing_docs)]
-pub struct CalcLengthPercentage {
- #[css(skip)]
- pub clamping_mode: AllowedNumericType,
- pub node: CalcNode,
-}
-
-impl CalcLengthPercentage {
- fn same_unit_length_as(a: &Self, b: &Self) -> Option<(CSSFloat, CSSFloat)> {
- use generic::CalcNodeLeaf;
-
- debug_assert_eq!(a.clamping_mode, b.clamping_mode);
- debug_assert_eq!(a.clamping_mode, AllowedNumericType::All);
-
- let a = a.node.as_leaf()?;
- let b = b.node.as_leaf()?;
-
- if a.sort_key() != b.sort_key() {
- return None;
- }
-
- let a = a.as_length()?.unitless_value();
- let b = b.as_length()?.unitless_value();
- return Some((a, b));
- }
-}
-
-impl SpecifiedValueInfo for CalcLengthPercentage {}
-
-impl PartialOrd for Leaf {
- fn partial_cmp(&self, other: &Self) -> Option<cmp::Ordering> {
- use self::Leaf::*;
-
- if std::mem::discriminant(self) != std::mem::discriminant(other) {
- return None;
- }
-
- match (self, other) {
- // NOTE: Percentages can't be compared reasonably here because the
- // percentage basis might be negative, see bug 1709018.
- // Conveniently, we only use this for <length-percentage> (for raw
- // percentages, we go through resolve()).
- (&Percentage(..), &Percentage(..)) => None,
- (&Length(ref one), &Length(ref other)) => one.partial_cmp(other),
- (&Angle(ref one), &Angle(ref other)) => one.degrees().partial_cmp(&other.degrees()),
- (&Time(ref one), &Time(ref other)) => one.seconds().partial_cmp(&other.seconds()),
- (&Resolution(ref one), &Resolution(ref other)) => one.dppx().partial_cmp(&other.dppx()),
- (&Number(ref one), &Number(ref other)) => one.partial_cmp(other),
- _ => {
- match *self {
- Length(..) | Percentage(..) | Angle(..) | Time(..) | Number(..) |
- Resolution(..) => {},
- }
- unsafe {
- debug_unreachable!("Forgot a branch?");
- }
- },
- }
- }
-}
-
-impl generic::CalcNodeLeaf for Leaf {
- fn unitless_value(&self) -> f32 {
- match *self {
- Self::Length(ref l) => l.unitless_value(),
- Self::Percentage(n) | Self::Number(n) => n,
- Self::Resolution(ref r) => r.dppx(),
- Self::Angle(ref a) => a.degrees(),
- Self::Time(ref t) => t.seconds(),
- }
- }
-
- fn sort_key(&self) -> SortKey {
- match *self {
- Self::Number(..) => SortKey::Number,
- Self::Percentage(..) => SortKey::Percentage,
- Self::Time(..) => SortKey::Sec,
- Self::Resolution(..) => SortKey::Dppx,
- Self::Angle(..) => SortKey::Deg,
- Self::Length(ref l) => match *l {
- NoCalcLength::Absolute(..) => SortKey::Px,
- NoCalcLength::FontRelative(ref relative) => match *relative {
- FontRelativeLength::Ch(..) => SortKey::Ch,
- FontRelativeLength::Em(..) => SortKey::Em,
- FontRelativeLength::Ex(..) => SortKey::Ex,
- FontRelativeLength::Cap(..) => SortKey::Cap,
- FontRelativeLength::Ic(..) => SortKey::Ic,
- FontRelativeLength::Rem(..) => SortKey::Rem,
- },
- NoCalcLength::ViewportPercentage(ref vp) => match *vp {
- ViewportPercentageLength::Vh(..) => SortKey::Vh,
- ViewportPercentageLength::Svh(..) => SortKey::Svh,
- ViewportPercentageLength::Lvh(..) => SortKey::Lvh,
- ViewportPercentageLength::Dvh(..) => SortKey::Dvh,
- ViewportPercentageLength::Vw(..) => SortKey::Vw,
- ViewportPercentageLength::Svw(..) => SortKey::Svw,
- ViewportPercentageLength::Lvw(..) => SortKey::Lvw,
- ViewportPercentageLength::Dvw(..) => SortKey::Dvw,
- ViewportPercentageLength::Vmax(..) => SortKey::Vmax,
- ViewportPercentageLength::Svmax(..) => SortKey::Svmax,
- ViewportPercentageLength::Lvmax(..) => SortKey::Lvmax,
- ViewportPercentageLength::Dvmax(..) => SortKey::Dvmax,
- ViewportPercentageLength::Vmin(..) => SortKey::Vmin,
- ViewportPercentageLength::Svmin(..) => SortKey::Svmin,
- ViewportPercentageLength::Lvmin(..) => SortKey::Lvmin,
- ViewportPercentageLength::Dvmin(..) => SortKey::Dvmin,
- ViewportPercentageLength::Vb(..) => SortKey::Vb,
- ViewportPercentageLength::Svb(..) => SortKey::Svb,
- ViewportPercentageLength::Lvb(..) => SortKey::Lvb,
- ViewportPercentageLength::Dvb(..) => SortKey::Dvb,
- ViewportPercentageLength::Vi(..) => SortKey::Vi,
- ViewportPercentageLength::Svi(..) => SortKey::Svi,
- ViewportPercentageLength::Lvi(..) => SortKey::Lvi,
- ViewportPercentageLength::Dvi(..) => SortKey::Dvi,
- },
- NoCalcLength::ContainerRelative(ref cq) => match *cq {
- ContainerRelativeLength::Cqw(..) => SortKey::Cqw,
- ContainerRelativeLength::Cqh(..) => SortKey::Cqh,
- ContainerRelativeLength::Cqi(..) => SortKey::Cqi,
- ContainerRelativeLength::Cqb(..) => SortKey::Cqb,
- ContainerRelativeLength::Cqmin(..) => SortKey::Cqmin,
- ContainerRelativeLength::Cqmax(..) => SortKey::Cqmax,
- },
- NoCalcLength::ServoCharacterWidth(..) => unreachable!(),
- },
- }
- }
-
- fn simplify(&mut self) {
- if let Self::Length(NoCalcLength::Absolute(ref mut abs)) = *self {
- *abs = AbsoluteLength::Px(abs.to_px());
- }
- }
-
- /// Tries to merge one sum to another, that is, perform `x` + `y`.
- ///
- /// Only handles leaf nodes, it's the caller's responsibility to simplify
- /// them before calling this if needed.
- fn try_sum_in_place(&mut self, other: &Self) -> Result<(), ()> {
- use self::Leaf::*;
-
- if std::mem::discriminant(self) != std::mem::discriminant(other) {
- return Err(());
- }
-
- match (self, other) {
- (&mut Number(ref mut one), &Number(ref other)) |
- (&mut Percentage(ref mut one), &Percentage(ref other)) => {
- *one += *other;
- },
- (&mut Angle(ref mut one), &Angle(ref other)) => {
- *one = specified::Angle::from_calc(one.degrees() + other.degrees());
- },
- (&mut Time(ref mut one), &Time(ref other)) => {
- *one = specified::Time::from_seconds(one.seconds() + other.seconds());
- },
- (&mut Resolution(ref mut one), &Resolution(ref other)) => {
- *one = specified::Resolution::from_dppx(one.dppx() + other.dppx());
- },
- (&mut Length(ref mut one), &Length(ref other)) => {
- *one = one.try_op(other, std::ops::Add::add)?;
- },
- _ => {
- match *other {
- Number(..) | Percentage(..) | Angle(..) | Time(..) | Resolution(..) |
- Length(..) => {},
- }
- unsafe {
- debug_unreachable!();
- }
- },
- }
-
- Ok(())
- }
-
- fn try_op<O>(&self, other: &Self, op: O) -> Result<Self, ()>
- where
- O: Fn(f32, f32) -> f32,
- {
- use self::Leaf::*;
-
- if std::mem::discriminant(self) != std::mem::discriminant(other) {
- return Err(());
- }
-
- match (self, other) {
- (&Number(one), &Number(other)) => {
- return Ok(Leaf::Number(op(one, other)));
- },
- (&Percentage(one), &Percentage(other)) => {
- return Ok(Leaf::Percentage(op(one, other)));
- },
- (&Angle(ref one), &Angle(ref other)) => {
- return Ok(Leaf::Angle(specified::Angle::from_calc(op(
- one.degrees(),
- other.degrees(),
- ))));
- },
- (&Resolution(ref one), &Resolution(ref other)) => {
- return Ok(Leaf::Resolution(specified::Resolution::from_dppx(op(
- one.dppx(),
- other.dppx(),
- ))));
- },
- (&Time(ref one), &Time(ref other)) => {
- return Ok(Leaf::Time(specified::Time::from_seconds(op(
- one.seconds(),
- other.seconds(),
- ))));
- },
- (&Length(ref one), &Length(ref other)) => {
- return Ok(Leaf::Length(one.try_op(other, op)?));
- },
- _ => {
- match *other {
- Number(..) | Percentage(..) | Angle(..) | Time(..) | Length(..) |
- Resolution(..) => {},
- }
- unsafe {
- debug_unreachable!();
- }
- },
- }
- }
-
- fn map(&mut self, mut op: impl FnMut(f32) -> f32) {
- match self {
- Leaf::Length(one) => *one = one.map(op),
- Leaf::Angle(one) => *one = specified::Angle::from_calc(op(one.degrees())),
- Leaf::Time(one) => *one = specified::Time::from_seconds(op(one.seconds())),
- Leaf::Resolution(one) => *one = specified::Resolution::from_dppx(op(one.dppx())),
- Leaf::Percentage(one) => *one = op(*one),
- Leaf::Number(one) => *one = op(*one),
- }
- }
-}
-
-/// A calc node representation for specified values.
-pub type CalcNode = generic::GenericCalcNode<Leaf>;
-
-impl CalcNode {
- /// Tries to parse a single element in the expression, that is, a
- /// `<length>`, `<angle>`, `<time>`, `<percentage>`, according to
- /// `allowed_units`.
- ///
- /// May return a "complex" `CalcNode`, in the presence of a parenthesized
- /// expression, for example.
- fn parse_one<'i, 't>(
- context: &ParserContext,
- input: &mut Parser<'i, 't>,
- allowed_units: CalcUnits,
- ) -> Result<Self, ParseError<'i>> {
- let location = input.current_source_location();
- match input.next()? {
- &Token::Number { value, .. } => Ok(CalcNode::Leaf(Leaf::Number(value))),
- &Token::Dimension {
- value, ref unit, ..
- } => {
- if allowed_units.intersects(CalcUnits::LENGTH) {
- if let Ok(l) = NoCalcLength::parse_dimension(context, value, unit) {
- return Ok(CalcNode::Leaf(Leaf::Length(l)));
- }
- }
- if allowed_units.intersects(CalcUnits::ANGLE) {
- if let Ok(a) = Angle::parse_dimension(value, unit, /* from_calc = */ true) {
- return Ok(CalcNode::Leaf(Leaf::Angle(a)));
- }
- }
- if allowed_units.intersects(CalcUnits::TIME) {
- if let Ok(t) = Time::parse_dimension(value, unit) {
- return Ok(CalcNode::Leaf(Leaf::Time(t)));
- }
- }
- if allowed_units.intersects(CalcUnits::RESOLUTION) {
- if let Ok(t) = Resolution::parse_dimension(value, unit) {
- return Ok(CalcNode::Leaf(Leaf::Resolution(t)));
- }
- }
- return Err(location.new_custom_error(StyleParseErrorKind::UnspecifiedError));
- },
- &Token::Percentage { unit_value, .. }
- if allowed_units.intersects(CalcUnits::PERCENTAGE) =>
- {
- Ok(CalcNode::Leaf(Leaf::Percentage(unit_value)))
- },
- &Token::ParenthesisBlock => input.parse_nested_block(|input| {
- CalcNode::parse_argument(context, input, allowed_units)
- }),
- &Token::Function(ref name) => {
- let function = CalcNode::math_function(context, name, location)?;
- CalcNode::parse(context, input, function, allowed_units)
- },
- &Token::Ident(ref ident) => {
- let number = match_ignore_ascii_case! { &**ident,
- "e" if trig_enabled() => std::f32::consts::E,
- "pi" if trig_enabled() => std::f32::consts::PI,
- "infinity" if nan_inf_enabled() => f32::INFINITY,
- "-infinity" if nan_inf_enabled() => f32::NEG_INFINITY,
- "nan" if nan_inf_enabled() => f32::NAN,
- _ => return Err(location.new_unexpected_token_error(Token::Ident(ident.clone()))),
- };
- Ok(CalcNode::Leaf(Leaf::Number(number)))
- },
- t => Err(location.new_unexpected_token_error(t.clone())),
- }
- }
-
- /// Parse a top-level `calc` expression, with all nested sub-expressions.
- ///
- /// This is in charge of parsing, for example, `2 + 3 * 100%`.
- fn parse<'i, 't>(
- context: &ParserContext,
- input: &mut Parser<'i, 't>,
- function: MathFunction,
- allowed_units: CalcUnits,
- ) -> Result<Self, ParseError<'i>> {
- // TODO: Do something different based on the function name. In
- // particular, for non-calc function we need to take a list of
- // comma-separated arguments and such.
- input.parse_nested_block(|input| {
- match function {
- MathFunction::Calc => Self::parse_argument(context, input, allowed_units),
- MathFunction::Clamp => {
- let min = Self::parse_argument(context, input, allowed_units)?;
- input.expect_comma()?;
- let center = Self::parse_argument(context, input, allowed_units)?;
- input.expect_comma()?;
- let max = Self::parse_argument(context, input, allowed_units)?;
- Ok(Self::Clamp {
- min: Box::new(min),
- center: Box::new(center),
- max: Box::new(max),
- })
- },
- MathFunction::Round => {
- let strategy = input.try_parse(parse_rounding_strategy);
-
- // <rounding-strategy> = nearest | up | down | to-zero
- // https://drafts.csswg.org/css-values-4/#calc-syntax
- fn parse_rounding_strategy<'i, 't>(
- input: &mut Parser<'i, 't>,
- ) -> Result<RoundingStrategy, ParseError<'i>> {
- Ok(try_match_ident_ignore_ascii_case! { input,
- "nearest" => RoundingStrategy::Nearest,
- "up" => RoundingStrategy::Up,
- "down" => RoundingStrategy::Down,
- "to-zero" => RoundingStrategy::ToZero,
- })
- }
-
- if strategy.is_ok() {
- input.expect_comma()?;
- }
-
- let value = Self::parse_argument(context, input, allowed_units)?;
- input.expect_comma()?;
- let step = Self::parse_argument(context, input, allowed_units)?;
-
- Ok(Self::Round {
- strategy: strategy.unwrap_or(RoundingStrategy::Nearest),
- value: Box::new(value),
- step: Box::new(step),
- })
- },
- MathFunction::Mod | MathFunction::Rem => {
- let dividend = Self::parse_argument(context, input, allowed_units)?;
- input.expect_comma()?;
- let divisor = Self::parse_argument(context, input, allowed_units)?;
-
- let op = match function {
- MathFunction::Mod => ModRemOp::Mod,
- MathFunction::Rem => ModRemOp::Rem,
- _ => unreachable!(),
- };
- Ok(Self::ModRem {
- dividend: Box::new(dividend),
- divisor: Box::new(divisor),
- op,
- })
- },
- MathFunction::Min | MathFunction::Max => {
- // TODO(emilio): The common case for parse_comma_separated
- // is just one element, but for min / max is two, really...
- //
- // Consider adding an API to cssparser to specify the
- // initial vector capacity?
- let arguments = input.parse_comma_separated(|input| {
- Self::parse_argument(context, input, allowed_units)
- })?;
-
- let op = match function {
- MathFunction::Min => MinMaxOp::Min,
- MathFunction::Max => MinMaxOp::Max,
- _ => unreachable!(),
- };
-
- Ok(Self::MinMax(arguments.into(), op))
- },
- MathFunction::Sin | MathFunction::Cos | MathFunction::Tan => {
- let a = Self::parse_angle_argument(context, input)?;
-
- let number = match function {
- MathFunction::Sin => a.sin(),
- MathFunction::Cos => a.cos(),
- MathFunction::Tan => a.tan(),
- _ => unsafe {
- debug_unreachable!("We just checked!");
- },
- };
-
- Ok(Self::Leaf(Leaf::Number(number)))
- },
- MathFunction::Asin | MathFunction::Acos | MathFunction::Atan => {
- let a = Self::parse_number_argument(context, input)?;
-
- let radians = match function {
- MathFunction::Asin => a.asin(),
- MathFunction::Acos => a.acos(),
- MathFunction::Atan => a.atan(),
- _ => unsafe {
- debug_unreachable!("We just checked!");
- },
- };
-
- Ok(Self::Leaf(Leaf::Angle(Angle::from_radians(radians))))
- },
- MathFunction::Atan2 => {
- let a = Self::parse_argument(context, input, CalcUnits::ALL)?;
- input.expect_comma()?;
- let b = Self::parse_argument(context, input, CalcUnits::ALL)?;
-
- let radians = Self::try_resolve(input, || {
- if let Ok(a) = a.to_number() {
- let b = b.to_number()?;
- return Ok(a.atan2(b));
- }
-
- if let Ok(a) = a.to_percentage() {
- let b = b.to_percentage()?;
- return Ok(a.atan2(b));
- }
-
- if let Ok(a) = a.to_time(None) {
- let b = b.to_time(None)?;
- return Ok(a.seconds().atan2(b.seconds()));
- }
-
- if let Ok(a) = a.to_angle() {
- let b = b.to_angle()?;
- return Ok(a.radians().atan2(b.radians()));
- }
-
- if let Ok(a) = a.to_resolution() {
- let b = b.to_resolution()?;
- return Ok(a.dppx().atan2(b.dppx()));
- }
-
- let a = a.into_length_or_percentage(AllowedNumericType::All)?;
- let b = b.into_length_or_percentage(AllowedNumericType::All)?;
- let (a, b) = CalcLengthPercentage::same_unit_length_as(&a, &b).ok_or(())?;
-
- Ok(a.atan2(b))
- })?;
-
- Ok(Self::Leaf(Leaf::Angle(Angle::from_radians(radians))))
- },
- MathFunction::Pow => {
- let a = Self::parse_number_argument(context, input)?;
- input.expect_comma()?;
- let b = Self::parse_number_argument(context, input)?;
-
- let number = a.powf(b);
-
- Ok(Self::Leaf(Leaf::Number(number)))
- },
- MathFunction::Sqrt => {
- let a = Self::parse_number_argument(context, input)?;
-
- let number = a.sqrt();
-
- Ok(Self::Leaf(Leaf::Number(number)))
- },
- MathFunction::Hypot => {
- let arguments = input.parse_comma_separated(|input| {
- Self::parse_argument(context, input, allowed_units)
- })?;
-
- Ok(Self::Hypot(arguments.into()))
- },
- MathFunction::Log => {
- let a = Self::parse_number_argument(context, input)?;
- let b = input
- .try_parse(|input| {
- input.expect_comma()?;
- Self::parse_number_argument(context, input)
- })
- .ok();
-
- let number = match b {
- Some(b) => a.log(b),
- None => a.ln(),
- };
-
- Ok(Self::Leaf(Leaf::Number(number)))
- },
- MathFunction::Exp => {
- let a = Self::parse_number_argument(context, input)?;
-
- let number = a.exp();
-
- Ok(Self::Leaf(Leaf::Number(number)))
- },
- }
- })
- }
-
- fn parse_angle_argument<'i, 't>(
- context: &ParserContext,
- input: &mut Parser<'i, 't>,
- ) -> Result<CSSFloat, ParseError<'i>> {
- let argument = Self::parse_argument(context, input, CalcUnits::ANGLE)?;
- argument
- .to_number()
- .or_else(|()| Ok(argument.to_angle()?.radians()))
- .map_err(|()| input.new_custom_error(StyleParseErrorKind::UnspecifiedError))
- }
-
- fn parse_number_argument<'i, 't>(
- context: &ParserContext,
- input: &mut Parser<'i, 't>,
- ) -> Result<CSSFloat, ParseError<'i>> {
- Self::parse_argument(context, input, CalcUnits::empty())?
- .to_number()
- .map_err(|()| input.new_custom_error(StyleParseErrorKind::UnspecifiedError))
- }
-
- fn parse_argument<'i, 't>(
- context: &ParserContext,
- input: &mut Parser<'i, 't>,
- allowed_units: CalcUnits,
- ) -> Result<Self, ParseError<'i>> {
- let mut sum = SmallVec::<[CalcNode; 1]>::new();
- sum.push(Self::parse_product(context, input, allowed_units)?);
-
- loop {
- let start = input.state();
- match input.next_including_whitespace() {
- Ok(&Token::WhiteSpace(_)) => {
- if input.is_exhausted() {
- break; // allow trailing whitespace
- }
- match *input.next()? {
- Token::Delim('+') => {
- sum.push(Self::parse_product(context, input, allowed_units)?);
- },
- Token::Delim('-') => {
- let mut rhs = Self::parse_product(context, input, allowed_units)?;
- rhs.negate();
- sum.push(rhs);
- },
- _ => {
- input.reset(&start);
- break;
- },
- }
- },
- _ => {
- input.reset(&start);
- break;
- },
- }
- }
-
- Ok(if sum.len() == 1 {
- sum.drain(..).next().unwrap()
- } else {
- Self::Sum(sum.into_boxed_slice().into())
- })
- }
-
- /// Parse a top-level `calc` expression, and all the products that may
- /// follow, and stop as soon as a non-product expression is found.
- ///
- /// This should parse correctly:
- ///
- /// * `2`
- /// * `2 * 2`
- /// * `2 * 2 + 2` (but will leave the `+ 2` unparsed).
- ///
- fn parse_product<'i, 't>(
- context: &ParserContext,
- input: &mut Parser<'i, 't>,
- allowed_units: CalcUnits,
- ) -> Result<Self, ParseError<'i>> {
- let mut node = Self::parse_one(context, input, allowed_units)?;
-
- loop {
- let start = input.state();
- match input.next() {
- Ok(&Token::Delim('*')) => {
- let rhs = Self::parse_one(context, input, allowed_units)?;
- if let Ok(rhs) = rhs.to_number() {
- node.mul_by(rhs);
- } else if let Ok(number) = node.to_number() {
- node = rhs;
- node.mul_by(number);
- } else {
- // One of the two parts of the multiplication has to be
- // a number, at least until we implement unit math.
- return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError));
- }
- },
- Ok(&Token::Delim('/')) => {
- let rhs = Self::parse_one(context, input, allowed_units)?;
- // Dividing by units is not ok.
- //
- // TODO(emilio): Eventually it should be.
- let number = match rhs.to_number() {
- Ok(n) if n != 0. || nan_inf_enabled() => n,
- _ => {
- return Err(
- input.new_custom_error(StyleParseErrorKind::UnspecifiedError)
- )
- },
- };
- node.mul_by(1. / number);
- },
- _ => {
- input.reset(&start);
- break;
- },
- }
- }
-
- Ok(node)
- }
-
- fn try_resolve<'i, 't, F>(
- input: &Parser<'i, 't>,
- closure: F,
- ) -> Result<CSSFloat, ParseError<'i>>
- where
- F: FnOnce() -> Result<CSSFloat, ()>,
- {
- closure().map_err(|()| input.new_custom_error(StyleParseErrorKind::UnspecifiedError))
- }
-
- /// Tries to simplify this expression into a `<length>` or `<percentage>`
- /// value.
- fn into_length_or_percentage(
- mut self,
- clamping_mode: AllowedNumericType,
- ) -> Result<CalcLengthPercentage, ()> {
- // Keep track of whether there's any invalid member of the calculation,
- // so as to reject the calculation properly at parse-time.
- let mut any_invalid = false;
- self.visit_depth_first(|node| {
- if let CalcNode::Leaf(ref l) = *node {
- any_invalid |= !matches!(*l, Leaf::Percentage(..) | Leaf::Length(..));
- }
- node.simplify_and_sort_direct_children();
- });
-
- if any_invalid {
- return Err(());
- }
-
- Ok(CalcLengthPercentage {
- clamping_mode,
- node: self,
- })
- }
-
- /// Tries to simplify this expression into a `<time>` value.
- fn to_time(&self, clamping_mode: Option<AllowedNumericType>) -> Result<Time, ()> {
- let seconds = self.resolve(|leaf| match *leaf {
- Leaf::Time(ref time) => Ok(time.seconds()),
- _ => Err(()),
- })?;
-
- Ok(Time::from_seconds_with_calc_clamping_mode(
- if nan_inf_enabled() {
- seconds
- } else {
- crate::values::normalize(seconds)
- },
- clamping_mode,
- ))
- }
-
- /// Tries to simplify the expression into a `<resolution>` value.
- fn to_resolution(&self) -> Result<Resolution, ()> {
- let dppx = self.resolve(|leaf| match *leaf {
- Leaf::Resolution(ref r) => Ok(r.dppx()),
- _ => Err(()),
- })?;
-
- Ok(Resolution::from_dppx_calc(if nan_inf_enabled() {
- dppx
- } else {
- crate::values::normalize(dppx)
- }))
- }
-
- /// Tries to simplify this expression into an `Angle` value.
- fn to_angle(&self) -> Result<Angle, ()> {
- let degrees = self.resolve(|leaf| match *leaf {
- Leaf::Angle(ref angle) => Ok(angle.degrees()),
- _ => Err(()),
- })?;
- let result = Angle::from_calc(if nan_inf_enabled() {
- degrees
- } else {
- crate::values::normalize(degrees)
- });
- Ok(result)
- }
-
- /// Tries to simplify this expression into a `<number>` value.
- fn to_number(&self) -> Result<CSSFloat, ()> {
- let number = self.resolve(|leaf| match *leaf {
- Leaf::Number(n) => Ok(n),
- _ => Err(()),
- })?;
- let result = if nan_inf_enabled() {
- number
- } else {
- crate::values::normalize(number)
- };
- Ok(result)
- }
-
- /// Tries to simplify this expression into a `<percentage>` value.
- fn to_percentage(&self) -> Result<CSSFloat, ()> {
- self.resolve(|leaf| match *leaf {
- Leaf::Percentage(p) => Ok(p),
- _ => Err(()),
- })
- }
-
- /// Given a function name, and the location from where the token came from,
- /// return a mathematical function corresponding to that name or an error.
- #[inline]
- pub fn math_function<'i>(
- context: &ParserContext,
- name: &CowRcStr<'i>,
- location: cssparser::SourceLocation,
- ) -> Result<MathFunction, ParseError<'i>> {
- use self::MathFunction::*;
-
- let function = match MathFunction::from_ident(&*name) {
- Ok(f) => f,
- Err(()) => {
- return Err(location.new_unexpected_token_error(Token::Function(name.clone())))
- },
- };
-
- let enabled = if context.chrome_rules_enabled() {
- true
- } else if matches!(function, Sin | Cos | Tan | Asin | Acos | Atan | Atan2) {
- trig_enabled()
- } else if matches!(function, Round) {
- static_prefs::pref!("layout.css.round.enabled")
- } else if matches!(function, Mod | Rem) {
- static_prefs::pref!("layout.css.mod-rem.enabled")
- } else if matches!(function, Pow | Sqrt | Hypot | Log | Exp) {
- static_prefs::pref!("layout.css.exp.enabled")
- } else {
- true
- };
-
- if !enabled {
- return Err(location.new_unexpected_token_error(Token::Function(name.clone())));
- }
-
- Ok(function)
- }
-
- /// Convenience parsing function for integers.
- pub fn parse_integer<'i, 't>(
- context: &ParserContext,
- input: &mut Parser<'i, 't>,
- function: MathFunction,
- ) -> Result<CSSInteger, ParseError<'i>> {
- Self::parse_number(context, input, function).map(|n| n.round() as CSSInteger)
- }
-
- /// Convenience parsing function for `<length> | <percentage>`.
- pub fn parse_length_or_percentage<'i, 't>(
- context: &ParserContext,
- input: &mut Parser<'i, 't>,
- clamping_mode: AllowedNumericType,
- function: MathFunction,
- ) -> Result<CalcLengthPercentage, ParseError<'i>> {
- Self::parse(context, input, function, CalcUnits::LENGTH_PERCENTAGE)?
- .into_length_or_percentage(clamping_mode)
- .map_err(|()| input.new_custom_error(StyleParseErrorKind::UnspecifiedError))
- }
-
- /// Convenience parsing function for percentages.
- pub fn parse_percentage<'i, 't>(
- context: &ParserContext,
- input: &mut Parser<'i, 't>,
- function: MathFunction,
- ) -> Result<CSSFloat, ParseError<'i>> {
- Self::parse(context, input, function, CalcUnits::PERCENTAGE)?
- .to_percentage()
- .map(crate::values::normalize)
- .map_err(|()| input.new_custom_error(StyleParseErrorKind::UnspecifiedError))
- }
-
- /// Convenience parsing function for `<length>`.
- pub fn parse_length<'i, 't>(
- context: &ParserContext,
- input: &mut Parser<'i, 't>,
- clamping_mode: AllowedNumericType,
- function: MathFunction,
- ) -> Result<CalcLengthPercentage, ParseError<'i>> {
- Self::parse(context, input, function, CalcUnits::LENGTH)?
- .into_length_or_percentage(clamping_mode)
- .map_err(|()| input.new_custom_error(StyleParseErrorKind::UnspecifiedError))
- }
-
- /// Convenience parsing function for `<number>`.
- pub fn parse_number<'i, 't>(
- context: &ParserContext,
- input: &mut Parser<'i, 't>,
- function: MathFunction,
- ) -> Result<CSSFloat, ParseError<'i>> {
- Self::parse(context, input, function, CalcUnits::empty())?
- .to_number()
- .map_err(|()| input.new_custom_error(StyleParseErrorKind::UnspecifiedError))
- }
-
- /// Convenience parsing function for `<angle>`.
- pub fn parse_angle<'i, 't>(
- context: &ParserContext,
- input: &mut Parser<'i, 't>,
- function: MathFunction,
- ) -> Result<Angle, ParseError<'i>> {
- Self::parse(context, input, function, CalcUnits::ANGLE)?
- .to_angle()
- .map_err(|()| input.new_custom_error(StyleParseErrorKind::UnspecifiedError))
- }
-
- /// Convenience parsing function for `<time>`.
- pub fn parse_time<'i, 't>(
- context: &ParserContext,
- input: &mut Parser<'i, 't>,
- clamping_mode: AllowedNumericType,
- function: MathFunction,
- ) -> Result<Time, ParseError<'i>> {
- Self::parse(context, input, function, CalcUnits::TIME)?
- .to_time(Some(clamping_mode))
- .map_err(|()| input.new_custom_error(StyleParseErrorKind::UnspecifiedError))
- }
-
- /// Convenience parsing function for `<resolution>`.
- pub fn parse_resolution<'i, 't>(
- context: &ParserContext,
- input: &mut Parser<'i, 't>,
- function: MathFunction,
- ) -> Result<Resolution, ParseError<'i>> {
- Self::parse(context, input, function, CalcUnits::RESOLUTION)?
- .to_resolution()
- .map_err(|()| input.new_custom_error(StyleParseErrorKind::UnspecifiedError))
- }
-
- /// Convenience parsing function for `<number>` or `<percentage>`.
- pub fn parse_number_or_percentage<'i, 't>(
- context: &ParserContext,
- input: &mut Parser<'i, 't>,
- function: MathFunction,
- ) -> Result<NumberOrPercentage, ParseError<'i>> {
- let node = Self::parse(context, input, function, CalcUnits::PERCENTAGE)?;
-
- if let Ok(value) = node.to_number() {
- return Ok(NumberOrPercentage::Number { value });
- }
-
- match node.to_percentage() {
- Ok(unit_value) => Ok(NumberOrPercentage::Percentage { unit_value }),
- Err(()) => Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError)),
- }
- }
-
- /// Convenience parsing function for `<number>` or `<angle>`.
- pub fn parse_angle_or_number<'i, 't>(
- context: &ParserContext,
- input: &mut Parser<'i, 't>,
- function: MathFunction,
- ) -> Result<AngleOrNumber, ParseError<'i>> {
- let node = Self::parse(context, input, function, CalcUnits::ANGLE)?;
-
- if let Ok(angle) = node.to_angle() {
- let degrees = angle.degrees();
- return Ok(AngleOrNumber::Angle { degrees });
- }
-
- match node.to_number() {
- Ok(value) => Ok(AngleOrNumber::Number { value }),
- Err(()) => Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError)),
- }
- }
-}
diff --git a/components/style/values/specified/color.rs b/components/style/values/specified/color.rs
deleted file mode 100644
index c84592a97b8..00000000000
--- a/components/style/values/specified/color.rs
+++ /dev/null
@@ -1,1184 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
-
-//! Specified color values.
-
-use super::AllowQuirks;
-use crate::color::mix::ColorInterpolationMethod;
-use crate::color::{AbsoluteColor, ColorComponents, ColorFlags, ColorSpace};
-use crate::media_queries::Device;
-use crate::parser::{Parse, ParserContext};
-use crate::values::computed::{Color as ComputedColor, Context, ToComputedValue};
-use crate::values::generics::color::{GenericCaretColor, GenericColorMix, GenericColorOrAuto};
-use crate::values::specified::calc::CalcNode;
-use crate::values::specified::Percentage;
-use crate::values::CustomIdent;
-use cssparser::{AngleOrNumber, Color as CSSParserColor, Parser, Token};
-use cssparser::{BasicParseErrorKind, NumberOrPercentage, ParseErrorKind};
-use itoa;
-use std::fmt::{self, Write};
-use std::io::Write as IoWrite;
-use style_traits::{CssType, CssWriter, KeywordsCollectFn, ParseError, StyleParseErrorKind};
-use style_traits::{SpecifiedValueInfo, ToCss, ValueParseErrorKind};
-
-/// A specified color-mix().
-pub type ColorMix = GenericColorMix<Color, Percentage>;
-
-impl ColorMix {
- fn parse<'i, 't>(
- context: &ParserContext,
- input: &mut Parser<'i, 't>,
- preserve_authored: PreserveAuthored,
- ) -> Result<Self, ParseError<'i>> {
- let enabled =
- context.chrome_rules_enabled() || static_prefs::pref!("layout.css.color-mix.enabled");
-
- if !enabled {
- return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError));
- }
-
- input.expect_function_matching("color-mix")?;
-
- input.parse_nested_block(|input| {
- let interpolation = ColorInterpolationMethod::parse(context, input)?;
- input.expect_comma()?;
-
- let try_parse_percentage = |input: &mut Parser| -> Option<Percentage> {
- input
- .try_parse(|input| Percentage::parse_zero_to_a_hundred(context, input))
- .ok()
- };
-
- let mut left_percentage = try_parse_percentage(input);
-
- let left = Color::parse_internal(context, input, preserve_authored)?;
- if left_percentage.is_none() {
- left_percentage = try_parse_percentage(input);
- }
-
- input.expect_comma()?;
-
- let mut right_percentage = try_parse_percentage(input);
-
- let right = Color::parse(context, input)?;
-
- if right_percentage.is_none() {
- right_percentage = try_parse_percentage(input);
- }
-
- let right_percentage = right_percentage
- .unwrap_or_else(|| Percentage::new(1.0 - left_percentage.map_or(0.5, |p| p.get())));
-
- let left_percentage =
- left_percentage.unwrap_or_else(|| Percentage::new(1.0 - right_percentage.get()));
-
- if left_percentage.get() + right_percentage.get() <= 0.0 {
- // If the percentages sum to zero, the function is invalid.
- return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError));
- }
-
- Ok(ColorMix {
- interpolation,
- left,
- left_percentage,
- right,
- right_percentage,
- normalize_weights: true,
- })
- })
- }
-}
-
-/// Container holding an absolute color and the text specified by an author.
-#[derive(Clone, Debug, MallocSizeOf, PartialEq, ToShmem)]
-pub struct Absolute {
- /// The specified color.
- pub color: AbsoluteColor,
- /// Authored representation.
- pub authored: Option<Box<str>>,
-}
-
-impl ToCss for Absolute {
- fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
- where
- W: Write,
- {
- if let Some(ref authored) = self.authored {
- dest.write_str(authored)
- } else {
- self.color.to_css(dest)
- }
- }
-}
-
-/// Specified color value
-#[derive(Clone, Debug, MallocSizeOf, PartialEq, ToShmem)]
-pub enum Color {
- /// The 'currentColor' keyword
- CurrentColor,
- /// An absolute color.
- /// https://w3c.github.io/csswg-drafts/css-color-4/#typedef-absolute-color-function
- Absolute(Box<Absolute>),
- /// A system color.
- #[cfg(feature = "gecko")]
- System(SystemColor),
- /// A color mix.
- ColorMix(Box<ColorMix>),
- /// Quirksmode-only rule for inheriting color from the body
- #[cfg(feature = "gecko")]
- InheritFromBodyQuirk,
-}
-
-impl From<AbsoluteColor> for Color {
- #[inline]
- fn from(value: AbsoluteColor) -> Self {
- Self::from_absolute_color(value)
- }
-}
-
-/// System colors. A bunch of these are ad-hoc, others come from Windows:
-///
-/// https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-getsyscolor
-///
-/// Others are HTML/CSS specific. Spec is:
-///
-/// https://drafts.csswg.org/css-color/#css-system-colors
-/// https://drafts.csswg.org/css-color/#deprecated-system-colors
-#[allow(missing_docs)]
-#[cfg(feature = "gecko")]
-#[derive(Clone, Copy, Debug, MallocSizeOf, Parse, PartialEq, ToCss, ToShmem)]
-#[repr(u8)]
-pub enum SystemColor {
- Activeborder,
- /// Background in the (active) titlebar.
- Activecaption,
- Appworkspace,
- Background,
- Buttonface,
- Buttonhighlight,
- Buttonshadow,
- Buttontext,
- Buttonborder,
- /// Text color in the (active) titlebar.
- Captiontext,
- #[parse(aliases = "-moz-field")]
- Field,
- /// Used for disabled field backgrounds.
- #[parse(condition = "ParserContext::in_ua_or_chrome_sheet")]
- MozDisabledfield,
- #[parse(aliases = "-moz-fieldtext")]
- Fieldtext,
-
- Mark,
- Marktext,
-
- /// Combobox widgets
- MozComboboxtext,
- MozCombobox,
-
- Graytext,
- Highlight,
- Highlighttext,
- Inactiveborder,
- /// Background in the (inactive) titlebar.
- Inactivecaption,
- /// Text color in the (inactive) titlebar.
- Inactivecaptiontext,
- Infobackground,
- Infotext,
- Menu,
- Menutext,
- Scrollbar,
- Threeddarkshadow,
- Threedface,
- Threedhighlight,
- Threedlightshadow,
- Threedshadow,
- Window,
- Windowframe,
- Windowtext,
- MozButtondefault,
- #[parse(aliases = "-moz-default-color")]
- Canvastext,
- #[parse(aliases = "-moz-default-background-color")]
- Canvas,
- MozDialog,
- MozDialogtext,
- /// Used to highlight valid regions to drop something onto.
- MozDragtargetzone,
- /// Used for selected but not focused cell backgrounds.
- #[parse(aliases = "-moz-html-cellhighlight")]
- MozCellhighlight,
- /// Used for selected but not focused cell text.
- #[parse(aliases = "-moz-html-cellhighlighttext")]
- MozCellhighlighttext,
- /// Used for selected and focused html cell backgrounds.
- Selecteditem,
- /// Used for selected and focused html cell text.
- Selecteditemtext,
- /// Used to button text background when hovered.
- MozButtonhoverface,
- /// Used to button text color when hovered.
- MozButtonhovertext,
- /// Used for menu item backgrounds when hovered.
- MozMenuhover,
- /// Used for menu item backgrounds when hovered and disabled.
- #[parse(condition = "ParserContext::in_ua_or_chrome_sheet")]
- MozMenuhoverdisabled,
- /// Used for menu item text when hovered.
- MozMenuhovertext,
- /// Used for menubar item text.
- MozMenubartext,
- /// Used for menubar item text when hovered.
- MozMenubarhovertext,
-
- /// On platforms where these colors are the same as -moz-field, use
- /// -moz-fieldtext as foreground color
- MozEventreerow,
- MozOddtreerow,
-
- /// Used for button text when pressed.
- #[parse(condition = "ParserContext::in_ua_or_chrome_sheet")]
- MozButtonactivetext,
-
- /// Used for button background when pressed.
- #[parse(condition = "ParserContext::in_ua_or_chrome_sheet")]
- MozButtonactiveface,
-
- /// Used for button background when disabled.
- #[parse(condition = "ParserContext::in_ua_or_chrome_sheet")]
- MozButtondisabledface,
-
- /// Background color of chrome toolbars in active windows.
- MozMacChromeActive,
- /// Background color of chrome toolbars in inactive windows.
- MozMacChromeInactive,
- /// Foreground color of default buttons.
- MozMacDefaultbuttontext,
- /// Ring color around text fields and lists.
- MozMacFocusring,
- /// Color used when mouse is over a menu item.
- MozMacMenuselect,
- /// Color used to do shadows on menu items.
- MozMacMenushadow,
- /// Color used to display text for disabled menu items.
- MozMacMenutextdisable,
- /// Color used to display text while mouse is over a menu item.
- MozMacMenutextselect,
- /// Text color of disabled text on toolbars.
- MozMacDisabledtoolbartext,
- /// Inactive light hightlight
- MozMacSecondaryhighlight,
-
- MozMacMenupopup,
- MozMacMenuitem,
- MozMacActiveMenuitem,
- MozMacSourceList,
- MozMacSourceListSelection,
- MozMacActiveSourceListSelection,
- MozMacTooltip,
-
- /// Theme accent color.
- /// https://drafts.csswg.org/css-color-4/#valdef-system-color-accentcolor
- Accentcolor,
-
- /// Foreground for the accent color.
- /// https://drafts.csswg.org/css-color-4/#valdef-system-color-accentcolortext
- Accentcolortext,
-
- /// The background-color for :autofill-ed inputs.
- #[parse(condition = "ParserContext::in_ua_or_chrome_sheet")]
- MozAutofillBackground,
-
- /// Media rebar text.
- MozWinMediatext,
- /// Communications rebar text.
- MozWinCommunicationstext,
-
- /// Hyperlink color extracted from the system, not affected by the
- /// browser.anchor_color user pref.
- ///
- /// There is no OS-specified safe background color for this text, but it is
- /// used regularly within Windows and the Gnome DE on Dialog and Window
- /// colors.
- MozNativehyperlinktext,
-
- /// As above, but visited link color.
- #[css(skip)]
- MozNativevisitedhyperlinktext,
-
- #[parse(aliases = "-moz-hyperlinktext")]
- Linktext,
- #[parse(aliases = "-moz-activehyperlinktext")]
- Activetext,
- #[parse(aliases = "-moz-visitedhyperlinktext")]
- Visitedtext,
-
- /// Color of tree column headers
- #[parse(condition = "ParserContext::in_ua_or_chrome_sheet")]
- MozColheadertext,
- #[parse(condition = "ParserContext::in_ua_or_chrome_sheet")]
- MozColheaderhovertext,
-
- #[parse(condition = "ParserContext::in_ua_or_chrome_sheet")]
- TextSelectDisabledBackground,
- #[css(skip)]
- TextSelectAttentionBackground,
- #[css(skip)]
- TextSelectAttentionForeground,
- #[css(skip)]
- TextHighlightBackground,
- #[css(skip)]
- TextHighlightForeground,
- #[css(skip)]
- IMERawInputBackground,
- #[css(skip)]
- IMERawInputForeground,
- #[css(skip)]
- IMERawInputUnderline,
- #[css(skip)]
- IMESelectedRawTextBackground,
- #[css(skip)]
- IMESelectedRawTextForeground,
- #[css(skip)]
- IMESelectedRawTextUnderline,
- #[css(skip)]
- IMEConvertedTextBackground,
- #[css(skip)]
- IMEConvertedTextForeground,
- #[css(skip)]
- IMEConvertedTextUnderline,
- #[css(skip)]
- IMESelectedConvertedTextBackground,
- #[css(skip)]
- IMESelectedConvertedTextForeground,
- #[css(skip)]
- IMESelectedConvertedTextUnderline,
- #[css(skip)]
- SpellCheckerUnderline,
- #[css(skip)]
- ThemedScrollbar,
- #[css(skip)]
- ThemedScrollbarInactive,
- #[css(skip)]
- ThemedScrollbarThumb,
- #[css(skip)]
- ThemedScrollbarThumbHover,
- #[css(skip)]
- ThemedScrollbarThumbActive,
- #[css(skip)]
- ThemedScrollbarThumbInactive,
-
- #[css(skip)]
- End, // Just for array-indexing purposes.
-}
-
-#[cfg(feature = "gecko")]
-impl SystemColor {
- #[inline]
- fn compute(&self, cx: &Context) -> ComputedColor {
- use crate::gecko::values::convert_nscolor_to_absolute_color;
- use crate::gecko_bindings::bindings;
-
- // TODO: We should avoid cloning here most likely, though it's
- // cheap-ish.
- let style_color_scheme = cx.style().get_inherited_ui().clone_color_scheme();
- let color = cx.device().system_nscolor(*self, &style_color_scheme);
- if color == bindings::NS_SAME_AS_FOREGROUND_COLOR {
- return ComputedColor::currentcolor();
- }
- ComputedColor::Absolute(convert_nscolor_to_absolute_color(color))
- }
-}
-
-#[inline]
-fn new_absolute(
- color_space: ColorSpace,
- c1: Option<f32>,
- c2: Option<f32>,
- c3: Option<f32>,
- alpha: Option<f32>,
-) -> Color {
- let mut flags = ColorFlags::empty();
-
- macro_rules! c {
- ($v:expr,$flag:tt) => {{
- if let Some(value) = $v {
- value
- } else {
- flags |= ColorFlags::$flag;
- 0.0
- }
- }};
- }
-
- let c1 = c!(c1, C1_IS_NONE);
- let c2 = c!(c2, C2_IS_NONE);
- let c3 = c!(c3, C3_IS_NONE);
- let alpha = c!(alpha, ALPHA_IS_NONE);
-
- let mut color = AbsoluteColor::new(color_space, ColorComponents(c1, c2, c3), alpha);
- color.flags |= flags;
- Color::Absolute(Box::new(Absolute {
- color,
- authored: None,
- }))
-}
-
-impl cssparser::FromParsedColor for Color {
- fn from_current_color() -> Self {
- Color::CurrentColor
- }
-
- fn from_rgba(red: Option<u8>, green: Option<u8>, blue: Option<u8>, alpha: Option<f32>) -> Self {
- new_absolute(
- ColorSpace::Srgb,
- red.map(|r| r as f32 / 255.0),
- green.map(|g| g as f32 / 255.0),
- blue.map(|b| b as f32 / 255.0),
- alpha,
- )
- }
-
- fn from_hsl(
- hue: Option<f32>,
- saturation: Option<f32>,
- lightness: Option<f32>,
- alpha: Option<f32>,
- ) -> Self {
- new_absolute(ColorSpace::Hsl, hue, saturation, lightness, alpha)
- }
-
- fn from_hwb(
- hue: Option<f32>,
- whiteness: Option<f32>,
- blackness: Option<f32>,
- alpha: Option<f32>,
- ) -> Self {
- new_absolute(ColorSpace::Hwb, hue, whiteness, blackness, alpha)
- }
-
- fn from_lab(
- lightness: Option<f32>,
- a: Option<f32>,
- b: Option<f32>,
- alpha: Option<f32>,
- ) -> Self {
- new_absolute(ColorSpace::Lab, lightness, a, b, alpha)
- }
-
- fn from_lch(
- lightness: Option<f32>,
- chroma: Option<f32>,
- hue: Option<f32>,
- alpha: Option<f32>,
- ) -> Self {
- new_absolute(ColorSpace::Lch, lightness, chroma, hue, alpha)
- }
-
- fn from_oklab(
- lightness: Option<f32>,
- a: Option<f32>,
- b: Option<f32>,
- alpha: Option<f32>,
- ) -> Self {
- new_absolute(ColorSpace::Oklab, lightness, a, b, alpha)
- }
-
- fn from_oklch(
- lightness: Option<f32>,
- chroma: Option<f32>,
- hue: Option<f32>,
- alpha: Option<f32>,
- ) -> Self {
- new_absolute(ColorSpace::Oklch, lightness, chroma, hue, alpha)
- }
-
- fn from_color_function(
- color_space: cssparser::PredefinedColorSpace,
- c1: Option<f32>,
- c2: Option<f32>,
- c3: Option<f32>,
- alpha: Option<f32>,
- ) -> Self {
- let mut result = new_absolute(color_space.into(), c1, c2, c3, alpha);
- if let Color::Absolute(ref mut absolute) = result {
- if matches!(absolute.color.color_space, ColorSpace::Srgb) {
- absolute.color.flags |= ColorFlags::AS_COLOR_FUNCTION;
- }
- }
- result
- }
-}
-
-struct ColorParser<'a, 'b: 'a>(&'a ParserContext<'b>);
-impl<'a, 'b: 'a, 'i: 'a> ::cssparser::ColorParser<'i> for ColorParser<'a, 'b> {
- type Output = Color;
- type Error = StyleParseErrorKind<'i>;
-
- fn parse_angle_or_number<'t>(
- &self,
- input: &mut Parser<'i, 't>,
- ) -> Result<AngleOrNumber, ParseError<'i>> {
- use crate::values::specified::Angle;
-
- let location = input.current_source_location();
- let token = input.next()?.clone();
- match token {
- Token::Dimension {
- value, ref unit, ..
- } => {
- let angle = Angle::parse_dimension(value, unit, /* from_calc = */ false);
-
- let degrees = match angle {
- Ok(angle) => angle.degrees(),
- Err(()) => return Err(location.new_unexpected_token_error(token.clone())),
- };
-
- Ok(AngleOrNumber::Angle { degrees })
- },
- Token::Number { value, .. } => Ok(AngleOrNumber::Number { value }),
- Token::Function(ref name) => {
- let function = CalcNode::math_function(self.0, name, location)?;
- CalcNode::parse_angle_or_number(self.0, input, function)
- },
- t => return Err(location.new_unexpected_token_error(t)),
- }
- }
-
- fn parse_percentage<'t>(&self, input: &mut Parser<'i, 't>) -> Result<f32, ParseError<'i>> {
- Ok(Percentage::parse(self.0, input)?.get())
- }
-
- fn parse_number<'t>(&self, input: &mut Parser<'i, 't>) -> Result<f32, ParseError<'i>> {
- use crate::values::specified::Number;
-
- Ok(Number::parse(self.0, input)?.get())
- }
-
- fn parse_number_or_percentage<'t>(
- &self,
- input: &mut Parser<'i, 't>,
- ) -> Result<NumberOrPercentage, ParseError<'i>> {
- let location = input.current_source_location();
-
- match *input.next()? {
- Token::Number { value, .. } => Ok(NumberOrPercentage::Number { value }),
- Token::Percentage { unit_value, .. } => {
- Ok(NumberOrPercentage::Percentage { unit_value })
- },
- Token::Function(ref name) => {
- let function = CalcNode::math_function(self.0, name, location)?;
- CalcNode::parse_number_or_percentage(self.0, input, function)
- },
- ref t => return Err(location.new_unexpected_token_error(t.clone())),
- }
- }
-}
-
-/// Whether to preserve authored colors during parsing. That's useful only if we
-/// plan to serialize the color back.
-enum PreserveAuthored {
- No,
- Yes,
-}
-
-impl Parse for Color {
- fn parse<'i, 't>(
- context: &ParserContext,
- input: &mut Parser<'i, 't>,
- ) -> Result<Self, ParseError<'i>> {
- Self::parse_internal(context, input, PreserveAuthored::Yes)
- }
-}
-
-impl Color {
- fn parse_internal<'i, 't>(
- context: &ParserContext,
- input: &mut Parser<'i, 't>,
- preserve_authored: PreserveAuthored,
- ) -> Result<Self, ParseError<'i>> {
- let authored = match preserve_authored {
- PreserveAuthored::No => None,
- PreserveAuthored::Yes => {
- // Currently we only store authored value for color keywords,
- // because all browsers serialize those values as keywords for
- // specified value.
- let start = input.state();
- let authored = input.expect_ident_cloned().ok();
- input.reset(&start);
- authored
- },
- };
-
- let color_parser = ColorParser(&*context);
- match input.try_parse(|i| cssparser::parse_color_with(&color_parser, i)) {
- Ok(mut color) => {
- if let Color::Absolute(ref mut absolute) = color {
- let enabled = {
- let is_legacy_color = matches!(
- absolute.color.color_space,
- ColorSpace::Srgb | ColorSpace::Hsl
- );
- let is_color_function =
- absolute.color.flags.contains(ColorFlags::AS_COLOR_FUNCTION);
- let pref_enabled = static_prefs::pref!("layout.css.more_color_4.enabled");
-
- (is_legacy_color && !is_color_function) || pref_enabled
- };
- if !enabled {
- return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError));
- }
-
- // Because we can't set the `authored` value at construction time, we have to set it
- // here.
- absolute.authored = authored.map(|s| s.to_ascii_lowercase().into_boxed_str());
- }
- Ok(color)
- },
- Err(e) => {
- #[cfg(feature = "gecko")]
- {
- if let Ok(system) = input.try_parse(|i| SystemColor::parse(context, i)) {
- return Ok(Color::System(system));
- }
- }
-
- if let Ok(mix) = input.try_parse(|i| ColorMix::parse(context, i, preserve_authored))
- {
- return Ok(Color::ColorMix(Box::new(mix)));
- }
-
- match e.kind {
- ParseErrorKind::Basic(BasicParseErrorKind::UnexpectedToken(t)) => {
- Err(e.location.new_custom_error(StyleParseErrorKind::ValueError(
- ValueParseErrorKind::InvalidColor(t),
- )))
- },
- _ => Err(e),
- }
- },
- }
- }
-
- /// Returns whether a given color is valid for authors.
- pub fn is_valid(context: &ParserContext, input: &mut Parser) -> bool {
- input
- .parse_entirely(|input| Self::parse_internal(context, input, PreserveAuthored::No))
- .is_ok()
- }
-
- /// Tries to parse a color and compute it with a given device.
- pub fn parse_and_compute(
- context: &ParserContext,
- input: &mut Parser,
- device: Option<&Device>,
- ) -> Option<ComputedColor> {
- use crate::error_reporting::ContextualParseError;
- let start = input.position();
- let result = input
- .parse_entirely(|input| Self::parse_internal(context, input, PreserveAuthored::No));
-
- let specified = match result {
- Ok(s) => s,
- Err(e) => {
- if !context.error_reporting_enabled() {
- return None;
- }
- // Ignore other kinds of errors that might be reported, such as
- // ParseErrorKind::Basic(BasicParseErrorKind::UnexpectedToken),
- // since Gecko didn't use to report those to the error console.
- //
- // TODO(emilio): Revise whether we want to keep this at all, we
- // use this only for canvas, this warnings are disabled by
- // default and not available on OffscreenCanvas anyways...
- if let ParseErrorKind::Custom(StyleParseErrorKind::ValueError(..)) = e.kind {
- let location = e.location.clone();
- let error = ContextualParseError::UnsupportedValue(input.slice_from(start), e);
- context.log_css_error(location, error);
- }
- return None;
- },
- };
-
- match device {
- Some(device) => {
- Context::for_media_query_evaluation(device, device.quirks_mode(), |context| {
- specified.to_computed_color(Some(&context))
- })
- },
- None => specified.to_computed_color(None),
- }
- }
-}
-
-impl ToCss for Color {
- fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
- where
- W: Write,
- {
- match *self {
- Color::CurrentColor => cssparser::ToCss::to_css(&CSSParserColor::CurrentColor, dest),
- Color::Absolute(ref absolute) => absolute.to_css(dest),
- Color::ColorMix(ref mix) => mix.to_css(dest),
- #[cfg(feature = "gecko")]
- Color::System(system) => system.to_css(dest),
- #[cfg(feature = "gecko")]
- Color::InheritFromBodyQuirk => Ok(()),
- }
- }
-}
-
-impl Color {
- /// Returns whether this color is allowed in forced-colors mode.
- pub fn honored_in_forced_colors_mode(&self, allow_transparent: bool) -> bool {
- match *self {
- #[cfg(feature = "gecko")]
- Self::InheritFromBodyQuirk => false,
- Self::CurrentColor => true,
- #[cfg(feature = "gecko")]
- Self::System(..) => true,
- Self::Absolute(ref absolute) => allow_transparent && absolute.color.alpha() == 0.0,
- Self::ColorMix(ref mix) => {
- mix.left.honored_in_forced_colors_mode(allow_transparent) &&
- mix.right.honored_in_forced_colors_mode(allow_transparent)
- },
- }
- }
-
- /// Returns currentcolor value.
- #[inline]
- pub fn currentcolor() -> Self {
- Self::CurrentColor
- }
-
- /// Returns transparent value.
- #[inline]
- pub fn transparent() -> Self {
- // We should probably set authored to "transparent", but maybe it doesn't matter.
- Self::from_absolute_color(AbsoluteColor::transparent())
- }
-
- /// Create a color from an [`AbsoluteColor`].
- pub fn from_absolute_color(color: AbsoluteColor) -> Self {
- Color::Absolute(Box::new(Absolute {
- color,
- authored: None,
- }))
- }
-
- /// Parse a color, with quirks.
- ///
- /// <https://quirks.spec.whatwg.org/#the-hashless-hex-color-quirk>
- pub fn parse_quirky<'i, 't>(
- context: &ParserContext,
- input: &mut Parser<'i, 't>,
- allow_quirks: AllowQuirks,
- ) -> Result<Self, ParseError<'i>> {
- input.try_parse(|i| Self::parse(context, i)).or_else(|e| {
- if !allow_quirks.allowed(context.quirks_mode) {
- return Err(e);
- }
- Color::parse_quirky_color(input).map_err(|_| e)
- })
- }
-
- /// Parse a <quirky-color> value.
- ///
- /// <https://quirks.spec.whatwg.org/#the-hashless-hex-color-quirk>
- fn parse_quirky_color<'i, 't>(input: &mut Parser<'i, 't>) -> Result<Self, ParseError<'i>> {
- let location = input.current_source_location();
- let (value, unit) = match *input.next()? {
- Token::Number {
- int_value: Some(integer),
- ..
- } => (integer, None),
- Token::Dimension {
- int_value: Some(integer),
- ref unit,
- ..
- } => (integer, Some(unit)),
- Token::Ident(ref ident) => {
- if ident.len() != 3 && ident.len() != 6 {
- return Err(location.new_custom_error(StyleParseErrorKind::UnspecifiedError));
- }
- return cssparser::parse_hash_color(ident.as_bytes()).map_err(|()| {
- location.new_custom_error(StyleParseErrorKind::UnspecifiedError)
- });
- },
- ref t => {
- return Err(location.new_unexpected_token_error(t.clone()));
- },
- };
- if value < 0 {
- return Err(location.new_custom_error(StyleParseErrorKind::UnspecifiedError));
- }
- let length = if value <= 9 {
- 1
- } else if value <= 99 {
- 2
- } else if value <= 999 {
- 3
- } else if value <= 9999 {
- 4
- } else if value <= 99999 {
- 5
- } else if value <= 999999 {
- 6
- } else {
- return Err(location.new_custom_error(StyleParseErrorKind::UnspecifiedError));
- };
- let total = length + unit.as_ref().map_or(0, |d| d.len());
- if total > 6 {
- return Err(location.new_custom_error(StyleParseErrorKind::UnspecifiedError));
- }
- let mut serialization = [b'0'; 6];
- let space_padding = 6 - total;
- let mut written = space_padding;
- let mut buf = itoa::Buffer::new();
- let s = buf.format(value);
- (&mut serialization[written..])
- .write_all(s.as_bytes())
- .unwrap();
- written += s.len();
- if let Some(unit) = unit {
- written += (&mut serialization[written..])
- .write(unit.as_bytes())
- .unwrap();
- }
- debug_assert_eq!(written, 6);
- cssparser::parse_hash_color(&serialization)
- .map_err(|()| location.new_custom_error(StyleParseErrorKind::UnspecifiedError))
- }
-}
-
-impl Color {
- /// Converts this Color into a ComputedColor.
- ///
- /// If `context` is `None`, and the specified color requires data from
- /// the context to resolve, then `None` is returned.
- pub fn to_computed_color(&self, context: Option<&Context>) -> Option<ComputedColor> {
- Some(match *self {
- Color::CurrentColor => ComputedColor::CurrentColor,
- Color::Absolute(ref absolute) => ComputedColor::Absolute(absolute.color),
- Color::ColorMix(ref mix) => {
- use crate::values::computed::percentage::Percentage;
-
- let left = mix.left.to_computed_color(context)?;
- let right = mix.right.to_computed_color(context)?;
-
- ComputedColor::from_color_mix(GenericColorMix {
- interpolation: mix.interpolation,
- left,
- left_percentage: Percentage(mix.left_percentage.get()),
- right,
- right_percentage: Percentage(mix.right_percentage.get()),
- normalize_weights: mix.normalize_weights,
- })
- },
- #[cfg(feature = "gecko")]
- Color::System(system) => system.compute(context?),
- #[cfg(feature = "gecko")]
- Color::InheritFromBodyQuirk => {
- ComputedColor::Absolute(context?.device().body_text_color())
- },
- })
- }
-}
-
-impl ToComputedValue for Color {
- type ComputedValue = ComputedColor;
-
- fn to_computed_value(&self, context: &Context) -> ComputedColor {
- self.to_computed_color(Some(context)).unwrap()
- }
-
- fn from_computed_value(computed: &ComputedColor) -> Self {
- match *computed {
- ComputedColor::Absolute(ref color) => Self::from_absolute_color(color.clone()),
- ComputedColor::CurrentColor => Color::CurrentColor,
- ComputedColor::ColorMix(ref mix) => {
- Color::ColorMix(Box::new(ToComputedValue::from_computed_value(&**mix)))
- },
- }
- }
-}
-
-/// Specified color value for `-moz-font-smoothing-background-color`.
-///
-/// This property does not support `currentcolor`. We could drop it at
-/// parse-time, but it's not exposed to the web so it doesn't really matter.
-///
-/// We resolve it to `transparent` instead.
-#[derive(Clone, Debug, MallocSizeOf, PartialEq, SpecifiedValueInfo, ToCss, ToShmem)]
-pub struct MozFontSmoothingBackgroundColor(pub Color);
-
-impl Parse for MozFontSmoothingBackgroundColor {
- fn parse<'i, 't>(
- context: &ParserContext,
- input: &mut Parser<'i, 't>,
- ) -> Result<Self, ParseError<'i>> {
- Color::parse(context, input).map(MozFontSmoothingBackgroundColor)
- }
-}
-
-impl ToComputedValue for MozFontSmoothingBackgroundColor {
- type ComputedValue = AbsoluteColor;
-
- fn to_computed_value(&self, context: &Context) -> Self::ComputedValue {
- self.0
- .to_computed_value(context)
- .resolve_to_absolute(&AbsoluteColor::transparent())
- }
-
- fn from_computed_value(computed: &Self::ComputedValue) -> Self {
- MozFontSmoothingBackgroundColor(Color::from_absolute_color(*computed))
- }
-}
-
-impl SpecifiedValueInfo for Color {
- const SUPPORTED_TYPES: u8 = CssType::COLOR;
-
- fn collect_completion_keywords(f: KeywordsCollectFn) {
- // We are not going to insert all the color names here. Caller and
- // devtools should take care of them. XXX Actually, transparent
- // should probably be handled that way as well.
- // XXX `currentColor` should really be `currentcolor`. But let's
- // keep it consistent with the old system for now.
- f(&[
- "rgb",
- "rgba",
- "hsl",
- "hsla",
- "hwb",
- "currentColor",
- "transparent",
- ]);
- if static_prefs::pref!("layout.css.color-mix.enabled") {
- f(&["color-mix"]);
- }
- if static_prefs::pref!("layout.css.more_color_4.enabled") {
- f(&["color", "lab", "lch", "oklab", "oklch"]);
- }
- }
-}
-
-/// Specified value for the "color" property, which resolves the `currentcolor`
-/// keyword to the parent color instead of self's color.
-#[cfg_attr(feature = "gecko", derive(MallocSizeOf))]
-#[derive(Clone, Debug, PartialEq, SpecifiedValueInfo, ToCss, ToShmem)]
-pub struct ColorPropertyValue(pub Color);
-
-impl ToComputedValue for ColorPropertyValue {
- type ComputedValue = AbsoluteColor;
-
- #[inline]
- fn to_computed_value(&self, context: &Context) -> Self::ComputedValue {
- let current_color = context.builder.get_parent_inherited_text().clone_color();
- self.0
- .to_computed_value(context)
- .resolve_to_absolute(&current_color)
- }
-
- #[inline]
- fn from_computed_value(computed: &Self::ComputedValue) -> Self {
- ColorPropertyValue(Color::from_absolute_color(*computed).into())
- }
-}
-
-impl Parse for ColorPropertyValue {
- fn parse<'i, 't>(
- context: &ParserContext,
- input: &mut Parser<'i, 't>,
- ) -> Result<Self, ParseError<'i>> {
- Color::parse_quirky(context, input, AllowQuirks::Yes).map(ColorPropertyValue)
- }
-}
-
-/// auto | <color>
-pub type ColorOrAuto = GenericColorOrAuto<Color>;
-
-/// caret-color
-pub type CaretColor = GenericCaretColor<Color>;
-
-impl Parse for CaretColor {
- fn parse<'i, 't>(
- context: &ParserContext,
- input: &mut Parser<'i, 't>,
- ) -> Result<Self, ParseError<'i>> {
- ColorOrAuto::parse(context, input).map(GenericCaretColor)
- }
-}
-
-bitflags! {
- /// Various flags to represent the color-scheme property in an efficient
- /// way.
- #[derive(Default, MallocSizeOf, SpecifiedValueInfo, ToComputedValue, ToResolvedValue, ToShmem)]
- #[repr(C)]
- #[value_info(other_values = "light,dark,only")]
- pub struct ColorSchemeFlags: u8 {
- /// Whether the author specified `light`.
- const LIGHT = 1 << 0;
- /// Whether the author specified `dark`.
- const DARK = 1 << 1;
- /// Whether the author specified `only`.
- const ONLY = 1 << 2;
- }
-}
-
-/// <https://drafts.csswg.org/css-color-adjust/#color-scheme-prop>
-#[derive(
- Clone,
- Debug,
- Default,
- MallocSizeOf,
- PartialEq,
- SpecifiedValueInfo,
- ToComputedValue,
- ToResolvedValue,
- ToShmem,
-)]
-#[repr(C)]
-#[value_info(other_values = "normal")]
-pub struct ColorScheme {
- #[ignore_malloc_size_of = "Arc"]
- idents: crate::ArcSlice<CustomIdent>,
- bits: ColorSchemeFlags,
-}
-
-impl ColorScheme {
- /// Returns the `normal` value.
- pub fn normal() -> Self {
- Self {
- idents: Default::default(),
- bits: ColorSchemeFlags::empty(),
- }
- }
-
- /// Returns the raw bitfield.
- pub fn raw_bits(&self) -> u8 {
- self.bits.bits
- }
-}
-
-impl Parse for ColorScheme {
- fn parse<'i, 't>(
- _: &ParserContext,
- input: &mut Parser<'i, 't>,
- ) -> Result<Self, ParseError<'i>> {
- let mut idents = vec![];
- let mut bits = ColorSchemeFlags::empty();
-
- let mut location = input.current_source_location();
- while let Ok(ident) = input.try_parse(|i| i.expect_ident_cloned()) {
- let mut is_only = false;
- match_ignore_ascii_case! { &ident,
- "normal" => {
- if idents.is_empty() && bits.is_empty() {
- return Ok(Self::normal());
- }
- return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError));
- },
- "light" => bits.insert(ColorSchemeFlags::LIGHT),
- "dark" => bits.insert(ColorSchemeFlags::DARK),
- "only" => {
- if bits.intersects(ColorSchemeFlags::ONLY) {
- return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError));
- }
- bits.insert(ColorSchemeFlags::ONLY);
- is_only = true;
- },
- _ => {},
- };
-
- if is_only {
- if !idents.is_empty() {
- // Only is allowed either at the beginning or at the end,
- // but not in the middle.
- break;
- }
- } else {
- idents.push(CustomIdent::from_ident(location, &ident, &[])?);
- }
- location = input.current_source_location();
- }
-
- if idents.is_empty() {
- return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError));
- }
-
- Ok(Self {
- idents: crate::ArcSlice::from_iter(idents.into_iter()),
- bits,
- })
- }
-}
-
-impl ToCss for ColorScheme {
- fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
- where
- W: Write,
- {
- if self.idents.is_empty() {
- debug_assert!(self.bits.is_empty());
- return dest.write_str("normal");
- }
- let mut first = true;
- for ident in self.idents.iter() {
- if !first {
- dest.write_char(' ')?;
- }
- first = false;
- ident.to_css(dest)?;
- }
- if self.bits.intersects(ColorSchemeFlags::ONLY) {
- dest.write_str(" only")?;
- }
- Ok(())
- }
-}
-
-/// https://drafts.csswg.org/css-color-adjust/#print-color-adjust
-#[derive(
- Clone,
- Copy,
- Debug,
- MallocSizeOf,
- Parse,
- PartialEq,
- SpecifiedValueInfo,
- ToCss,
- ToComputedValue,
- ToResolvedValue,
- ToShmem,
-)]
-#[repr(u8)]
-pub enum PrintColorAdjust {
- /// Ignore backgrounds and darken text.
- Economy,
- /// Respect specified colors.
- Exact,
-}
-
-/// https://drafts.csswg.org/css-color-adjust-1/#forced-color-adjust-prop
-#[derive(
- Clone,
- Copy,
- Debug,
- MallocSizeOf,
- Parse,
- PartialEq,
- SpecifiedValueInfo,
- ToCss,
- ToComputedValue,
- ToResolvedValue,
- ToShmem,
-)]
-#[repr(u8)]
-pub enum ForcedColorAdjust {
- /// Adjust colors if needed.
- Auto,
- /// Respect specified colors.
- None,
-}
diff --git a/components/style/values/specified/column.rs b/components/style/values/specified/column.rs
deleted file mode 100644
index 2dd7bb0144d..00000000000
--- a/components/style/values/specified/column.rs
+++ /dev/null
@@ -1,11 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
-
-//! Specified types for the column properties.
-
-use crate::values::generics::column::ColumnCount as GenericColumnCount;
-use crate::values::specified::PositiveInteger;
-
-/// A specified type for `column-count` values.
-pub type ColumnCount = GenericColumnCount<PositiveInteger>;
diff --git a/components/style/values/specified/counters.rs b/components/style/values/specified/counters.rs
deleted file mode 100644
index 4dd949b6b1e..00000000000
--- a/components/style/values/specified/counters.rs
+++ /dev/null
@@ -1,281 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
-
-//! Specified types for counter properties.
-
-#[cfg(feature = "servo")]
-use crate::computed_values::list_style_type::T as ListStyleType;
-use crate::parser::{Parse, ParserContext};
-use crate::values::generics::counters as generics;
-use crate::values::generics::counters::CounterPair;
-#[cfg(feature = "gecko")]
-use crate::values::generics::CounterStyle;
-use crate::values::specified::image::Image;
-use crate::values::specified::Attr;
-use crate::values::specified::Integer;
-use crate::values::CustomIdent;
-use cssparser::{Parser, Token};
-use selectors::parser::SelectorParseErrorKind;
-use style_traits::{ParseError, StyleParseErrorKind};
-
-#[derive(PartialEq)]
-enum CounterType {
- Increment,
- Set,
- Reset,
-}
-
-impl CounterType {
- fn default_value(&self) -> i32 {
- match *self {
- Self::Increment => 1,
- Self::Reset | Self::Set => 0,
- }
- }
-}
-
-/// A specified value for the `counter-increment` property.
-pub type CounterIncrement = generics::GenericCounterIncrement<Integer>;
-
-impl Parse for CounterIncrement {
- fn parse<'i, 't>(
- context: &ParserContext,
- input: &mut Parser<'i, 't>,
- ) -> Result<Self, ParseError<'i>> {
- Ok(Self::new(parse_counters(
- context,
- input,
- CounterType::Increment,
- )?))
- }
-}
-
-/// A specified value for the `counter-set` property.
-pub type CounterSet = generics::GenericCounterSet<Integer>;
-
-impl Parse for CounterSet {
- fn parse<'i, 't>(
- context: &ParserContext,
- input: &mut Parser<'i, 't>,
- ) -> Result<Self, ParseError<'i>> {
- Ok(Self::new(parse_counters(context, input, CounterType::Set)?))
- }
-}
-
-/// A specified value for the `counter-reset` property.
-pub type CounterReset = generics::GenericCounterReset<Integer>;
-
-impl Parse for CounterReset {
- fn parse<'i, 't>(
- context: &ParserContext,
- input: &mut Parser<'i, 't>,
- ) -> Result<Self, ParseError<'i>> {
- Ok(Self::new(parse_counters(
- context,
- input,
- CounterType::Reset,
- )?))
- }
-}
-
-fn parse_counters<'i, 't>(
- context: &ParserContext,
- input: &mut Parser<'i, 't>,
- counter_type: CounterType,
-) -> Result<Vec<CounterPair<Integer>>, ParseError<'i>> {
- if input
- .try_parse(|input| input.expect_ident_matching("none"))
- .is_ok()
- {
- return Ok(vec![]);
- }
-
- let mut counters = Vec::new();
- loop {
- let location = input.current_source_location();
- let (name, is_reversed) = match input.next() {
- Ok(&Token::Ident(ref ident)) => {
- (CustomIdent::from_ident(location, ident, &["none"])?, false)
- },
- Ok(&Token::Function(ref name))
- if counter_type == CounterType::Reset && name.eq_ignore_ascii_case("reversed") =>
- {
- input.parse_nested_block(|input| {
- let location = input.current_source_location();
- Ok((
- CustomIdent::from_ident(location, input.expect_ident()?, &["none"])?,
- true,
- ))
- })?
- },
- Ok(t) => {
- let t = t.clone();
- return Err(location.new_unexpected_token_error(t));
- },
- Err(_) => break,
- };
-
- let value = match input.try_parse(|input| Integer::parse(context, input)) {
- Ok(start) => {
- if start.value == i32::min_value() {
- // The spec says that values must be clamped to the valid range,
- // and we reserve i32::min_value() as an internal magic value.
- // https://drafts.csswg.org/css-lists/#auto-numbering
- Integer::new(i32::min_value() + 1)
- } else {
- start
- }
- },
- _ => Integer::new(if is_reversed {
- i32::min_value()
- } else {
- counter_type.default_value()
- }),
- };
- counters.push(CounterPair {
- name,
- value,
- is_reversed,
- });
- }
-
- if !counters.is_empty() {
- Ok(counters)
- } else {
- Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError))
- }
-}
-
-/// The specified value for the `content` property.
-pub type Content = generics::GenericContent<Image>;
-
-/// The specified value for a content item in the `content` property.
-pub type ContentItem = generics::GenericContentItem<Image>;
-
-impl Content {
- #[cfg(feature = "servo")]
- fn parse_counter_style(_: &ParserContext, input: &mut Parser) -> ListStyleType {
- input
- .try_parse(|input| {
- input.expect_comma()?;
- ListStyleType::parse(input)
- })
- .unwrap_or(ListStyleType::Decimal)
- }
-
- #[cfg(feature = "gecko")]
- fn parse_counter_style(context: &ParserContext, input: &mut Parser) -> CounterStyle {
- input
- .try_parse(|input| {
- input.expect_comma()?;
- CounterStyle::parse(context, input)
- })
- .unwrap_or(CounterStyle::decimal())
- }
-}
-
-impl Parse for Content {
- // normal | none | [ <string> | <counter> | open-quote | close-quote | no-open-quote |
- // no-close-quote ]+
- // TODO: <uri>, attr(<identifier>)
- #[cfg_attr(feature = "servo", allow(unused_mut))]
- fn parse<'i, 't>(
- context: &ParserContext,
- input: &mut Parser<'i, 't>,
- ) -> Result<Self, ParseError<'i>> {
- if input
- .try_parse(|input| input.expect_ident_matching("normal"))
- .is_ok()
- {
- return Ok(generics::Content::Normal);
- }
- if input
- .try_parse(|input| input.expect_ident_matching("none"))
- .is_ok()
- {
- return Ok(generics::Content::None);
- }
-
- let mut content = vec![];
- let mut has_alt_content = false;
- loop {
- {
- if let Ok(image) = input.try_parse(|i| Image::parse_forbid_none(context, i)) {
- content.push(generics::ContentItem::Image(image));
- continue;
- }
- }
- match input.next() {
- Ok(&Token::QuotedString(ref value)) => {
- content.push(generics::ContentItem::String(
- value.as_ref().to_owned().into(),
- ));
- },
- Ok(&Token::Function(ref name)) => {
- let result = match_ignore_ascii_case! { &name,
- "counter" => input.parse_nested_block(|input| {
- let location = input.current_source_location();
- let name = CustomIdent::from_ident(location, input.expect_ident()?, &[])?;
- let style = Content::parse_counter_style(context, input);
- Ok(generics::ContentItem::Counter(name, style))
- }),
- "counters" => input.parse_nested_block(|input| {
- let location = input.current_source_location();
- let name = CustomIdent::from_ident(location, input.expect_ident()?, &[])?;
- input.expect_comma()?;
- let separator = input.expect_string()?.as_ref().to_owned().into();
- let style = Content::parse_counter_style(context, input);
- Ok(generics::ContentItem::Counters(name, separator, style))
- }),
- "attr" => input.parse_nested_block(|input| {
- Ok(generics::ContentItem::Attr(Attr::parse_function(context, input)?))
- }),
- _ => {
- use style_traits::StyleParseErrorKind;
- let name = name.clone();
- return Err(input.new_custom_error(
- StyleParseErrorKind::UnexpectedFunction(name),
- ))
- }
- }?;
- content.push(result);
- },
- Ok(&Token::Ident(ref ident)) => {
- content.push(match_ignore_ascii_case! { &ident,
- "open-quote" => generics::ContentItem::OpenQuote,
- "close-quote" => generics::ContentItem::CloseQuote,
- "no-open-quote" => generics::ContentItem::NoOpenQuote,
- "no-close-quote" => generics::ContentItem::NoCloseQuote,
- #[cfg(feature = "gecko")]
- "-moz-alt-content" => {
- has_alt_content = true;
- generics::ContentItem::MozAltContent
- },
- #[cfg(feature = "gecko")]
- "-moz-label-content" if context.chrome_rules_enabled() => {
- has_alt_content = true;
- generics::ContentItem::MozLabelContent
- },
- _ =>{
- let ident = ident.clone();
- return Err(input.new_custom_error(
- SelectorParseErrorKind::UnexpectedIdent(ident)
- ));
- }
- });
- },
- Err(_) => break,
- Ok(t) => {
- let t = t.clone();
- return Err(input.new_unexpected_token_error(t));
- },
- }
- }
- // We don't allow to parse `-moz-alt-content` in multiple positions.
- if content.is_empty() || (has_alt_content && content.len() != 1) {
- return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError));
- }
- Ok(generics::Content::Items(content.into()))
- }
-}
diff --git a/components/style/values/specified/easing.rs b/components/style/values/specified/easing.rs
deleted file mode 100644
index 8021e3b7dcb..00000000000
--- a/components/style/values/specified/easing.rs
+++ /dev/null
@@ -1,252 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
-
-//! Specified types for CSS Easing functions.
-use crate::parser::{Parse, ParserContext};
-use crate::piecewise_linear::{PiecewiseLinearFunction, PiecewiseLinearFunctionBuildParameters};
-use crate::values::computed::easing::TimingFunction as ComputedTimingFunction;
-use crate::values::computed::{Context, ToComputedValue};
-use crate::values::generics::easing::TimingFunction as GenericTimingFunction;
-use crate::values::generics::easing::{StepPosition, TimingKeyword};
-use crate::values::specified::{Integer, Number, Percentage};
-use cssparser::{Delimiter, Parser, Token};
-use selectors::parser::SelectorParseErrorKind;
-use std::iter::FromIterator;
-use style_traits::{ParseError, StyleParseErrorKind};
-
-/// An entry for linear easing function.
-#[derive(Clone, Copy, Debug, MallocSizeOf, PartialEq, SpecifiedValueInfo, ToCss, ToShmem)]
-pub struct LinearStop {
- /// Output of the function at the given point.
- pub output: Number,
- /// Playback progress at which this output is given.
- #[css(skip_if = "Option::is_none")]
- pub input: Option<Percentage>,
-}
-
-/// A list of specified linear stops.
-#[derive(Clone, Default, Debug, MallocSizeOf, PartialEq, SpecifiedValueInfo, ToCss, ToShmem)]
-#[css(comma)]
-pub struct LinearStops {
- #[css(iterable)]
- entries: crate::OwnedSlice<LinearStop>,
-}
-
-impl LinearStops {
- fn new(list: crate::OwnedSlice<LinearStop>) -> Self {
- LinearStops { entries: list }
- }
-}
-
-/// A specified timing function.
-pub type TimingFunction = GenericTimingFunction<Integer, Number, LinearStops>;
-
-#[cfg(feature = "gecko")]
-fn linear_timing_function_enabled() -> bool {
- static_prefs::pref!("layout.css.linear-easing-function.enabled")
-}
-
-#[cfg(feature = "servo")]
-fn linear_timing_function_enabled() -> bool {
- false
-}
-
-impl Parse for TimingFunction {
- fn parse<'i, 't>(
- context: &ParserContext,
- input: &mut Parser<'i, 't>,
- ) -> Result<Self, ParseError<'i>> {
- if let Ok(keyword) = input.try_parse(TimingKeyword::parse) {
- return Ok(GenericTimingFunction::Keyword(keyword));
- }
- if let Ok(ident) = input.try_parse(|i| i.expect_ident_cloned()) {
- let position = match_ignore_ascii_case! { &ident,
- "step-start" => StepPosition::Start,
- "step-end" => StepPosition::End,
- _ => {
- return Err(input.new_custom_error(
- SelectorParseErrorKind::UnexpectedIdent(ident.clone())
- ));
- },
- };
- return Ok(GenericTimingFunction::Steps(Integer::new(1), position));
- }
- let location = input.current_source_location();
- let function = input.expect_function()?.clone();
- input.parse_nested_block(move |i| {
- match_ignore_ascii_case! { &function,
- "cubic-bezier" => Self::parse_cubic_bezier(context, i),
- "steps" => Self::parse_steps(context, i),
- "linear" => Self::parse_linear_function(context, i),
- _ => Err(location.new_custom_error(StyleParseErrorKind::UnexpectedFunction(function.clone()))),
- }
- })
- }
-}
-
-impl TimingFunction {
- fn parse_cubic_bezier<'i, 't>(
- context: &ParserContext,
- input: &mut Parser<'i, 't>,
- ) -> Result<Self, ParseError<'i>> {
- let x1 = Number::parse(context, input)?;
- input.expect_comma()?;
- let y1 = Number::parse(context, input)?;
- input.expect_comma()?;
- let x2 = Number::parse(context, input)?;
- input.expect_comma()?;
- let y2 = Number::parse(context, input)?;
-
- if x1.get() < 0.0 || x1.get() > 1.0 || x2.get() < 0.0 || x2.get() > 1.0 {
- return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError));
- }
-
- Ok(GenericTimingFunction::CubicBezier { x1, y1, x2, y2 })
- }
-
- fn parse_steps<'i, 't>(
- context: &ParserContext,
- input: &mut Parser<'i, 't>,
- ) -> Result<Self, ParseError<'i>> {
- let steps = Integer::parse_positive(context, input)?;
- let position = input
- .try_parse(|i| {
- i.expect_comma()?;
- StepPosition::parse(context, i)
- })
- .unwrap_or(StepPosition::End);
-
- // jump-none accepts a positive integer greater than 1.
- // FIXME(emilio): The spec asks us to avoid rejecting it at parse
- // time except until computed value time.
- //
- // It's not totally clear it's worth it though, and no other browser
- // does this.
- if position == StepPosition::JumpNone && steps.value() <= 1 {
- return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError));
- }
- Ok(GenericTimingFunction::Steps(steps, position))
- }
-
- fn parse_linear_function<'i, 't>(
- context: &ParserContext,
- input: &mut Parser<'i, 't>,
- ) -> Result<Self, ParseError<'i>> {
- if !linear_timing_function_enabled() {
- return Err(input.new_custom_error(StyleParseErrorKind::ExperimentalProperty));
- }
- let mut result = vec![];
- // Closely follows `parse_comma_separated`, but can generate multiple entries for one comma-separated entry.
- loop {
- input.parse_until_before(Delimiter::Comma, |i| {
- let mut input_start = i.try_parse(|i| Percentage::parse(context, i)).ok();
- let mut input_end = i.try_parse(|i| Percentage::parse(context, i)).ok();
-
- let output = Number::parse(context, i)?;
- if input_start.is_none() {
- debug_assert!(input_end.is_none(), "Input end parsed without input start?");
- input_start = i.try_parse(|i| Percentage::parse(context, i)).ok();
- input_end = i.try_parse(|i| Percentage::parse(context, i)).ok();
- }
- result.push(LinearStop {
- output,
- input: input_start.into(),
- });
- if input_end.is_some() {
- debug_assert!(
- input_start.is_some(),
- "Input end valid but not input start?"
- );
- result.push(LinearStop {
- output,
- input: input_end.into(),
- });
- }
-
- Ok(())
- })?;
-
- match input.next() {
- Err(_) => break,
- Ok(&Token::Comma) => continue,
- Ok(_) => unreachable!(),
- }
- }
- if result.len() < 2 {
- return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError));
- }
-
- Ok(GenericTimingFunction::LinearFunction(LinearStops::new(
- crate::OwnedSlice::from(result),
- )))
- }
-}
-
-impl LinearStop {
- /// Convert this type to entries that can be used to build PiecewiseLinearFunction.
- pub fn to_piecewise_linear_build_parameters(
- x: &LinearStop,
- ) -> PiecewiseLinearFunctionBuildParameters {
- (x.output.get(), x.input.map(|x| x.get()))
- }
-}
-
-// We need this for converting the specified TimingFunction into computed TimingFunction without
-// Context (for some FFIs in glue.rs). In fact, we don't really need Context to get the computed
-// value of TimingFunction.
-impl TimingFunction {
- /// Generate the ComputedTimingFunction without Context.
- pub fn to_computed_value_without_context(&self) -> ComputedTimingFunction {
- match &self {
- GenericTimingFunction::Steps(steps, pos) => {
- GenericTimingFunction::Steps(steps.value(), *pos)
- },
- GenericTimingFunction::CubicBezier { x1, y1, x2, y2 } => {
- GenericTimingFunction::CubicBezier {
- x1: x1.get(),
- y1: y1.get(),
- x2: x2.get(),
- y2: y2.get(),
- }
- },
- GenericTimingFunction::Keyword(keyword) => GenericTimingFunction::Keyword(*keyword),
- GenericTimingFunction::LinearFunction(steps) => {
- GenericTimingFunction::LinearFunction(PiecewiseLinearFunction::from_iter(
- steps
- .entries
- .iter()
- .map(|e| LinearStop::to_piecewise_linear_build_parameters(e)),
- ))
- },
- }
- }
-}
-
-impl ToComputedValue for TimingFunction {
- type ComputedValue = ComputedTimingFunction;
- fn to_computed_value(&self, _: &Context) -> Self::ComputedValue {
- self.to_computed_value_without_context()
- }
-
- fn from_computed_value(computed: &Self::ComputedValue) -> Self {
- match &computed {
- ComputedTimingFunction::Steps(steps, pos) => Self::Steps(Integer::new(*steps), *pos),
- ComputedTimingFunction::CubicBezier { x1, y1, x2, y2 } => Self::CubicBezier {
- x1: Number::new(*x1),
- y1: Number::new(*y1),
- x2: Number::new(*x2),
- y2: Number::new(*y2),
- },
- ComputedTimingFunction::Keyword(keyword) => GenericTimingFunction::Keyword(*keyword),
- ComputedTimingFunction::LinearFunction(function) => {
- GenericTimingFunction::LinearFunction(LinearStops {
- entries: crate::OwnedSlice::from_iter(function.iter().map(|e| LinearStop {
- output: Number::new(e.y),
- input: Some(Percentage::new(e.x)).into(),
- })),
- })
- },
- }
- }
-}
diff --git a/components/style/values/specified/effects.rs b/components/style/values/specified/effects.rs
deleted file mode 100644
index 79a19d3209c..00000000000
--- a/components/style/values/specified/effects.rs
+++ /dev/null
@@ -1,450 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
-
-//! Specified types for CSS values related to effects.
-
-use crate::parser::{Parse, ParserContext};
-use crate::values::computed::effects::BoxShadow as ComputedBoxShadow;
-use crate::values::computed::effects::SimpleShadow as ComputedSimpleShadow;
-#[cfg(feature = "gecko")]
-use crate::values::computed::url::ComputedUrl;
-use crate::values::computed::Angle as ComputedAngle;
-use crate::values::computed::CSSPixelLength as ComputedCSSPixelLength;
-use crate::values::computed::Filter as ComputedFilter;
-use crate::values::computed::NonNegativeLength as ComputedNonNegativeLength;
-use crate::values::computed::NonNegativeNumber as ComputedNonNegativeNumber;
-use crate::values::computed::ZeroToOneNumber as ComputedZeroToOneNumber;
-use crate::values::computed::{Context, ToComputedValue};
-use crate::values::generics::effects::BoxShadow as GenericBoxShadow;
-use crate::values::generics::effects::Filter as GenericFilter;
-use crate::values::generics::effects::SimpleShadow as GenericSimpleShadow;
-use crate::values::generics::NonNegative;
-use crate::values::specified::color::Color;
-use crate::values::specified::length::{Length, NonNegativeLength};
-#[cfg(feature = "gecko")]
-use crate::values::specified::url::SpecifiedUrl;
-use crate::values::specified::{Angle, Number, NumberOrPercentage};
-#[cfg(feature = "servo")]
-use crate::values::Impossible;
-use crate::Zero;
-use cssparser::{self, BasicParseErrorKind, Parser, Token};
-use style_traits::{ParseError, StyleParseErrorKind, ValueParseErrorKind};
-
-/// A specified value for a single shadow of the `box-shadow` property.
-pub type BoxShadow =
- GenericBoxShadow<Option<Color>, Length, Option<NonNegativeLength>, Option<Length>>;
-
-/// A specified value for a single `filter`.
-#[cfg(feature = "gecko")]
-pub type SpecifiedFilter = GenericFilter<
- Angle,
- NonNegativeFactor,
- ZeroToOneFactor,
- NonNegativeLength,
- SimpleShadow,
- SpecifiedUrl,
->;
-
-/// A specified value for a single `filter`.
-#[cfg(feature = "servo")]
-pub type SpecifiedFilter = GenericFilter<
- Angle,
- NonNegativeFactor,
- ZeroToOneFactor,
- NonNegativeLength,
- SimpleShadow,
- Impossible,
->;
-
-pub use self::SpecifiedFilter as Filter;
-
-/// A value for the `<factor>` parts in `Filter`.
-#[derive(Clone, Debug, MallocSizeOf, PartialEq, SpecifiedValueInfo, ToCss, ToShmem)]
-pub struct NonNegativeFactor(NumberOrPercentage);
-
-/// A value for the `<factor>` parts in `Filter` which clamps to one.
-#[derive(Clone, Debug, MallocSizeOf, PartialEq, SpecifiedValueInfo, ToCss, ToShmem)]
-pub struct ZeroToOneFactor(NumberOrPercentage);
-
-/// Clamp the value to 1 if the value is over 100%.
-#[inline]
-fn clamp_to_one(number: NumberOrPercentage) -> NumberOrPercentage {
- match number {
- NumberOrPercentage::Percentage(percent) => {
- NumberOrPercentage::Percentage(percent.clamp_to_hundred())
- },
- NumberOrPercentage::Number(number) => NumberOrPercentage::Number(number.clamp_to_one()),
- }
-}
-
-macro_rules! factor_impl_common {
- ($ty:ty, $computed_ty:ty) => {
- impl $ty {
- #[inline]
- fn one() -> Self {
- Self(NumberOrPercentage::Number(Number::new(1.)))
- }
- }
-
- impl ToComputedValue for $ty {
- type ComputedValue = $computed_ty;
-
- #[inline]
- fn to_computed_value(&self, context: &Context) -> Self::ComputedValue {
- use crate::values::computed::NumberOrPercentage;
- match self.0.to_computed_value(context) {
- NumberOrPercentage::Number(n) => n.into(),
- NumberOrPercentage::Percentage(p) => p.0.into(),
- }
- }
-
- #[inline]
- fn from_computed_value(computed: &Self::ComputedValue) -> Self {
- Self(NumberOrPercentage::Number(
- ToComputedValue::from_computed_value(&computed.0),
- ))
- }
- }
- };
-}
-factor_impl_common!(NonNegativeFactor, ComputedNonNegativeNumber);
-factor_impl_common!(ZeroToOneFactor, ComputedZeroToOneNumber);
-
-impl Parse for NonNegativeFactor {
- #[inline]
- fn parse<'i, 't>(
- context: &ParserContext,
- input: &mut Parser<'i, 't>,
- ) -> Result<Self, ParseError<'i>> {
- NumberOrPercentage::parse_non_negative(context, input).map(Self)
- }
-}
-
-impl Parse for ZeroToOneFactor {
- #[inline]
- fn parse<'i, 't>(
- context: &ParserContext,
- input: &mut Parser<'i, 't>,
- ) -> Result<Self, ParseError<'i>> {
- NumberOrPercentage::parse_non_negative(context, input)
- .map(clamp_to_one)
- .map(Self)
- }
-}
-
-/// A specified value for the `drop-shadow()` filter.
-pub type SimpleShadow = GenericSimpleShadow<Option<Color>, Length, Option<NonNegativeLength>>;
-
-impl Parse for BoxShadow {
- fn parse<'i, 't>(
- context: &ParserContext,
- input: &mut Parser<'i, 't>,
- ) -> Result<Self, ParseError<'i>> {
- let mut lengths = None;
- let mut color = None;
- let mut inset = false;
-
- loop {
- if !inset {
- if input
- .try_parse(|input| input.expect_ident_matching("inset"))
- .is_ok()
- {
- inset = true;
- continue;
- }
- }
- if lengths.is_none() {
- let value = input.try_parse::<_, _, ParseError>(|i| {
- let horizontal = Length::parse(context, i)?;
- let vertical = Length::parse(context, i)?;
- let (blur, spread) =
- match i.try_parse(|i| Length::parse_non_negative(context, i)) {
- Ok(blur) => {
- let spread = i.try_parse(|i| Length::parse(context, i)).ok();
- (Some(blur.into()), spread)
- },
- Err(_) => (None, None),
- };
- Ok((horizontal, vertical, blur, spread))
- });
- if let Ok(value) = value {
- lengths = Some(value);
- continue;
- }
- }
- if color.is_none() {
- if let Ok(value) = input.try_parse(|i| Color::parse(context, i)) {
- color = Some(value);
- continue;
- }
- }
- break;
- }
-
- let lengths =
- lengths.ok_or(input.new_custom_error(StyleParseErrorKind::UnspecifiedError))?;
- Ok(BoxShadow {
- base: SimpleShadow {
- color: color,
- horizontal: lengths.0,
- vertical: lengths.1,
- blur: lengths.2,
- },
- spread: lengths.3,
- inset: inset,
- })
- }
-}
-
-impl ToComputedValue for BoxShadow {
- type ComputedValue = ComputedBoxShadow;
-
- #[inline]
- fn to_computed_value(&self, context: &Context) -> Self::ComputedValue {
- ComputedBoxShadow {
- base: self.base.to_computed_value(context),
- spread: self
- .spread
- .as_ref()
- .unwrap_or(&Length::zero())
- .to_computed_value(context),
- inset: self.inset,
- }
- }
-
- #[inline]
- fn from_computed_value(computed: &ComputedBoxShadow) -> Self {
- BoxShadow {
- base: ToComputedValue::from_computed_value(&computed.base),
- spread: Some(ToComputedValue::from_computed_value(&computed.spread)),
- inset: computed.inset,
- }
- }
-}
-
-// We need this for converting the specified Filter into computed Filter without Context (for
-// some FFIs in glue.rs). This can fail because in some circumstances, we still need Context to
-// determine the computed value.
-impl Filter {
- /// Generate the ComputedFilter without Context.
- pub fn to_computed_value_without_context(&self) -> Result<ComputedFilter, ()> {
- match *self {
- Filter::Blur(ref length) => Ok(ComputedFilter::Blur(ComputedNonNegativeLength::new(
- length.0.to_computed_pixel_length_without_context()?,
- ))),
- Filter::Brightness(ref factor) => Ok(ComputedFilter::Brightness(
- ComputedNonNegativeNumber::from(factor.0.to_number().get()),
- )),
- Filter::Contrast(ref factor) => Ok(ComputedFilter::Contrast(
- ComputedNonNegativeNumber::from(factor.0.to_number().get()),
- )),
- Filter::Grayscale(ref factor) => Ok(ComputedFilter::Grayscale(
- ComputedZeroToOneNumber::from(factor.0.to_number().get()),
- )),
- Filter::HueRotate(ref angle) => Ok(ComputedFilter::HueRotate(
- ComputedAngle::from_degrees(angle.degrees()),
- )),
- Filter::Invert(ref factor) => Ok(ComputedFilter::Invert(
- ComputedZeroToOneNumber::from(factor.0.to_number().get()),
- )),
- Filter::Opacity(ref factor) => Ok(ComputedFilter::Opacity(
- ComputedZeroToOneNumber::from(factor.0.to_number().get()),
- )),
- Filter::Saturate(ref factor) => Ok(ComputedFilter::Saturate(
- ComputedNonNegativeNumber::from(factor.0.to_number().get()),
- )),
- Filter::Sepia(ref factor) => Ok(ComputedFilter::Sepia(ComputedZeroToOneNumber::from(
- factor.0.to_number().get(),
- ))),
- Filter::DropShadow(ref shadow) => {
- if cfg!(feature = "gecko") {
- let color = match shadow
- .color
- .as_ref()
- .unwrap_or(&Color::currentcolor())
- .to_computed_color(None)
- {
- Some(c) => c,
- None => return Err(()),
- };
-
- let horizontal = ComputedCSSPixelLength::new(
- shadow
- .horizontal
- .to_computed_pixel_length_without_context()?,
- );
- let vertical = ComputedCSSPixelLength::new(
- shadow.vertical.to_computed_pixel_length_without_context()?,
- );
- let blur = ComputedNonNegativeLength::new(
- shadow
- .blur
- .as_ref()
- .unwrap_or(&NonNegativeLength::zero())
- .0
- .to_computed_pixel_length_without_context()?,
- );
-
- Ok(ComputedFilter::DropShadow(ComputedSimpleShadow {
- color,
- horizontal,
- vertical,
- blur,
- }))
- } else {
- Err(())
- }
- },
- #[cfg(feature = "gecko")]
- Filter::Url(ref url) => Ok(ComputedFilter::Url(ComputedUrl(url.clone()))),
- #[cfg(feature = "servo")]
- Filter::Url(_) => Err(()),
- }
- }
-}
-
-impl Parse for Filter {
- #[inline]
- fn parse<'i, 't>(
- context: &ParserContext,
- input: &mut Parser<'i, 't>,
- ) -> Result<Self, ParseError<'i>> {
- #[cfg(feature = "gecko")]
- {
- if let Ok(url) = input.try_parse(|i| SpecifiedUrl::parse(context, i)) {
- return Ok(GenericFilter::Url(url));
- }
- }
- let location = input.current_source_location();
- let function = match input.expect_function() {
- Ok(f) => f.clone(),
- Err(cssparser::BasicParseError {
- kind: BasicParseErrorKind::UnexpectedToken(t),
- location,
- }) => return Err(location.new_custom_error(ValueParseErrorKind::InvalidFilter(t))),
- Err(e) => return Err(e.into()),
- };
- input.parse_nested_block(|i| {
- match_ignore_ascii_case! { &*function,
- "blur" => Ok(GenericFilter::Blur(
- i.try_parse(|i| NonNegativeLength::parse(context, i))
- .unwrap_or(Zero::zero()),
- )),
- "brightness" => Ok(GenericFilter::Brightness(
- i.try_parse(|i| NonNegativeFactor::parse(context, i))
- .unwrap_or(NonNegativeFactor::one()),
- )),
- "contrast" => Ok(GenericFilter::Contrast(
- i.try_parse(|i| NonNegativeFactor::parse(context, i))
- .unwrap_or(NonNegativeFactor::one()),
- )),
- "grayscale" => {
- // Values of amount over 100% are allowed but UAs must clamp the values to 1.
- // https://drafts.fxtf.org/filter-effects/#funcdef-filter-grayscale
- Ok(GenericFilter::Grayscale(
- i.try_parse(|i| ZeroToOneFactor::parse(context, i))
- .unwrap_or(ZeroToOneFactor::one()),
- ))
- },
- "hue-rotate" => {
- // We allow unitless zero here, see:
- // https://github.com/w3c/fxtf-drafts/issues/228
- Ok(GenericFilter::HueRotate(
- i.try_parse(|i| Angle::parse_with_unitless(context, i))
- .unwrap_or(Zero::zero()),
- ))
- },
- "invert" => {
- // Values of amount over 100% are allowed but UAs must clamp the values to 1.
- // https://drafts.fxtf.org/filter-effects/#funcdef-filter-invert
- Ok(GenericFilter::Invert(
- i.try_parse(|i| ZeroToOneFactor::parse(context, i))
- .unwrap_or(ZeroToOneFactor::one()),
- ))
- },
- "opacity" => {
- // Values of amount over 100% are allowed but UAs must clamp the values to 1.
- // https://drafts.fxtf.org/filter-effects/#funcdef-filter-opacity
- Ok(GenericFilter::Opacity(
- i.try_parse(|i| ZeroToOneFactor::parse(context, i))
- .unwrap_or(ZeroToOneFactor::one()),
- ))
- },
- "saturate" => Ok(GenericFilter::Saturate(
- i.try_parse(|i| NonNegativeFactor::parse(context, i))
- .unwrap_or(NonNegativeFactor::one()),
- )),
- "sepia" => {
- // Values of amount over 100% are allowed but UAs must clamp the values to 1.
- // https://drafts.fxtf.org/filter-effects/#funcdef-filter-sepia
- Ok(GenericFilter::Sepia(
- i.try_parse(|i| ZeroToOneFactor::parse(context, i))
- .unwrap_or(ZeroToOneFactor::one()),
- ))
- },
- "drop-shadow" => Ok(GenericFilter::DropShadow(Parse::parse(context, i)?)),
- _ => Err(location.new_custom_error(
- ValueParseErrorKind::InvalidFilter(Token::Function(function.clone()))
- )),
- }
- })
- }
-}
-
-impl Parse for SimpleShadow {
- #[inline]
- fn parse<'i, 't>(
- context: &ParserContext,
- input: &mut Parser<'i, 't>,
- ) -> Result<Self, ParseError<'i>> {
- let color = input.try_parse(|i| Color::parse(context, i)).ok();
- let horizontal = Length::parse(context, input)?;
- let vertical = Length::parse(context, input)?;
- let blur = input
- .try_parse(|i| Length::parse_non_negative(context, i))
- .ok();
- let blur = blur.map(NonNegative::<Length>);
- let color = color.or_else(|| input.try_parse(|i| Color::parse(context, i)).ok());
-
- Ok(SimpleShadow {
- color,
- horizontal,
- vertical,
- blur,
- })
- }
-}
-
-impl ToComputedValue for SimpleShadow {
- type ComputedValue = ComputedSimpleShadow;
-
- #[inline]
- fn to_computed_value(&self, context: &Context) -> Self::ComputedValue {
- ComputedSimpleShadow {
- color: self
- .color
- .as_ref()
- .unwrap_or(&Color::currentcolor())
- .to_computed_value(context),
- horizontal: self.horizontal.to_computed_value(context),
- vertical: self.vertical.to_computed_value(context),
- blur: self
- .blur
- .as_ref()
- .unwrap_or(&NonNegativeLength::zero())
- .to_computed_value(context),
- }
- }
-
- #[inline]
- fn from_computed_value(computed: &Self::ComputedValue) -> Self {
- SimpleShadow {
- color: Some(ToComputedValue::from_computed_value(&computed.color)),
- horizontal: ToComputedValue::from_computed_value(&computed.horizontal),
- vertical: ToComputedValue::from_computed_value(&computed.vertical),
- blur: Some(ToComputedValue::from_computed_value(&computed.blur)),
- }
- }
-}
diff --git a/components/style/values/specified/flex.rs b/components/style/values/specified/flex.rs
deleted file mode 100644
index 7c767cdf34b..00000000000
--- a/components/style/values/specified/flex.rs
+++ /dev/null
@@ -1,25 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
-
-//! Specified types for CSS values related to flexbox.
-
-use crate::values::generics::flex::FlexBasis as GenericFlexBasis;
-use crate::values::specified::Size;
-
-/// A specified value for the `flex-basis` property.
-pub type FlexBasis = GenericFlexBasis<Size>;
-
-impl FlexBasis {
- /// `auto`
- #[inline]
- pub fn auto() -> Self {
- GenericFlexBasis::Size(Size::auto())
- }
-
- /// `0%`
- #[inline]
- pub fn zero_percent() -> Self {
- GenericFlexBasis::Size(Size::zero_percent())
- }
-}
diff --git a/components/style/values/specified/font.rs b/components/style/values/specified/font.rs
deleted file mode 100644
index abb5da2c1c3..00000000000
--- a/components/style/values/specified/font.rs
+++ /dev/null
@@ -1,2125 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
-
-//! Specified values for font properties
-
-#[cfg(feature = "gecko")]
-use crate::context::QuirksMode;
-use crate::parser::{Parse, ParserContext};
-use crate::values::computed::font::{FamilyName, FontFamilyList, SingleFontFamily};
-use crate::values::computed::Percentage as ComputedPercentage;
-use crate::values::computed::{font as computed, Length, NonNegativeLength};
-use crate::values::computed::{CSSPixelLength, Context, ToComputedValue};
-use crate::values::generics::font::VariationValue;
-use crate::values::generics::font::{
- self as generics, FeatureTagValue, FontSettings, FontTag, GenericFontSizeAdjust,
-};
-use crate::values::generics::NonNegative;
-use crate::values::specified::length::{FontBaseSize, PX_PER_PT};
-use crate::values::specified::{AllowQuirks, Angle, Integer, LengthPercentage};
-use crate::values::specified::{NoCalcLength, NonNegativeNumber, NonNegativePercentage, Number};
-use crate::values::{serialize_atom_identifier, CustomIdent, SelectorParseErrorKind};
-use crate::Atom;
-use cssparser::{Parser, Token};
-#[cfg(feature = "gecko")]
-use malloc_size_of::{MallocSizeOf, MallocSizeOfOps, MallocUnconditionalSizeOf};
-use std::fmt::{self, Write};
-use style_traits::values::SequenceWriter;
-use style_traits::{CssWriter, KeywordsCollectFn, ParseError};
-use style_traits::{SpecifiedValueInfo, StyleParseErrorKind, ToCss};
-
-// FIXME(emilio): The system font code is copy-pasta, and should be cleaned up.
-macro_rules! system_font_methods {
- ($ty:ident, $field:ident) => {
- system_font_methods!($ty);
-
- fn compute_system(&self, _context: &Context) -> <$ty as ToComputedValue>::ComputedValue {
- debug_assert!(matches!(*self, $ty::System(..)));
- #[cfg(feature = "gecko")]
- {
- _context.cached_system_font.as_ref().unwrap().$field.clone()
- }
- #[cfg(feature = "servo")]
- {
- unreachable!()
- }
- }
- };
-
- ($ty:ident) => {
- /// Get a specified value that represents a system font.
- pub fn system_font(f: SystemFont) -> Self {
- $ty::System(f)
- }
-
- /// Retreive a SystemFont from the specified value.
- pub fn get_system(&self) -> Option<SystemFont> {
- if let $ty::System(s) = *self {
- Some(s)
- } else {
- None
- }
- }
- };
-}
-
-/// System fonts.
-#[repr(u8)]
-#[derive(
- Clone, Copy, Debug, Eq, Hash, MallocSizeOf, Parse, PartialEq, SpecifiedValueInfo, ToCss, ToShmem
-)]
-#[allow(missing_docs)]
-#[cfg(feature = "gecko")]
-pub enum SystemFont {
- /// https://drafts.csswg.org/css-fonts/#valdef-font-caption
- Caption,
- /// https://drafts.csswg.org/css-fonts/#valdef-font-icon
- Icon,
- /// https://drafts.csswg.org/css-fonts/#valdef-font-menu
- Menu,
- /// https://drafts.csswg.org/css-fonts/#valdef-font-message-box
- MessageBox,
- /// https://drafts.csswg.org/css-fonts/#valdef-font-small-caption
- SmallCaption,
- /// https://drafts.csswg.org/css-fonts/#valdef-font-status-bar
- StatusBar,
- /// Internal system font, used by the `<menupopup>`s on macOS.
- #[parse(condition = "ParserContext::in_ua_or_chrome_sheet")]
- MozPullDownMenu,
- /// Internal system font, used for `<button>` elements.
- #[parse(condition = "ParserContext::in_ua_or_chrome_sheet")]
- MozButton,
- /// Internal font, used by `<select>` elements.
- #[parse(condition = "ParserContext::in_ua_or_chrome_sheet")]
- MozList,
- /// Internal font, used by `<input>` elements.
- #[parse(condition = "ParserContext::in_ua_or_chrome_sheet")]
- MozField,
- #[css(skip)]
- End, // Just for indexing purposes.
-}
-
-// We don't parse system fonts in servo, but in the interest of not
-// littering a lot of code with `if engine == "gecko"` conditionals,
-// we have a dummy system font module that does nothing
-
-#[derive(
- Clone, Copy, Debug, Eq, Hash, MallocSizeOf, PartialEq, SpecifiedValueInfo, ToCss, ToShmem
-)]
-#[allow(missing_docs)]
-#[cfg(feature = "servo")]
-/// void enum for system font, can never exist
-pub enum SystemFont {}
-
-#[allow(missing_docs)]
-#[cfg(feature = "servo")]
-impl SystemFont {
- pub fn parse(_: &mut Parser) -> Result<Self, ()> {
- Err(())
- }
-}
-
-const DEFAULT_SCRIPT_MIN_SIZE_PT: u32 = 8;
-const DEFAULT_SCRIPT_SIZE_MULTIPLIER: f64 = 0.71;
-
-/// The minimum font-weight value per:
-///
-/// https://drafts.csswg.org/css-fonts-4/#font-weight-numeric-values
-pub const MIN_FONT_WEIGHT: f32 = 1.;
-
-/// The maximum font-weight value per:
-///
-/// https://drafts.csswg.org/css-fonts-4/#font-weight-numeric-values
-pub const MAX_FONT_WEIGHT: f32 = 1000.;
-
-/// A specified font-weight value.
-///
-/// https://drafts.csswg.org/css-fonts-4/#propdef-font-weight
-#[derive(
- Clone, Copy, Debug, MallocSizeOf, Parse, PartialEq, SpecifiedValueInfo, ToCss, ToShmem,
-)]
-pub enum FontWeight {
- /// `<font-weight-absolute>`
- Absolute(AbsoluteFontWeight),
- /// Bolder variant
- Bolder,
- /// Lighter variant
- Lighter,
- /// System font variant.
- #[css(skip)]
- System(SystemFont),
-}
-
-impl FontWeight {
- system_font_methods!(FontWeight, font_weight);
-
- /// `normal`
- #[inline]
- pub fn normal() -> Self {
- FontWeight::Absolute(AbsoluteFontWeight::Normal)
- }
-
- /// Get a specified FontWeight from a gecko keyword
- pub fn from_gecko_keyword(kw: u32) -> Self {
- debug_assert!(kw % 100 == 0);
- debug_assert!(kw as f32 <= MAX_FONT_WEIGHT);
- FontWeight::Absolute(AbsoluteFontWeight::Weight(Number::new(kw as f32)))
- }
-}
-
-impl ToComputedValue for FontWeight {
- type ComputedValue = computed::FontWeight;
-
- #[inline]
- fn to_computed_value(&self, context: &Context) -> Self::ComputedValue {
- match *self {
- FontWeight::Absolute(ref abs) => abs.compute(),
- FontWeight::Bolder => context
- .builder
- .get_parent_font()
- .clone_font_weight()
- .bolder(),
- FontWeight::Lighter => context
- .builder
- .get_parent_font()
- .clone_font_weight()
- .lighter(),
- FontWeight::System(_) => self.compute_system(context),
- }
- }
-
- #[inline]
- fn from_computed_value(computed: &computed::FontWeight) -> Self {
- FontWeight::Absolute(AbsoluteFontWeight::Weight(Number::from_computed_value(
- &computed.value(),
- )))
- }
-}
-
-/// An absolute font-weight value for a @font-face rule.
-///
-/// https://drafts.csswg.org/css-fonts-4/#font-weight-absolute-values
-#[derive(Clone, Copy, Debug, MallocSizeOf, PartialEq, SpecifiedValueInfo, ToCss, ToShmem)]
-pub enum AbsoluteFontWeight {
- /// A `<number>`, with the additional constraints specified in:
- ///
- /// https://drafts.csswg.org/css-fonts-4/#font-weight-numeric-values
- Weight(Number),
- /// Normal font weight. Same as 400.
- Normal,
- /// Bold font weight. Same as 700.
- Bold,
-}
-
-impl AbsoluteFontWeight {
- /// Returns the computed value for this absolute font weight.
- pub fn compute(&self) -> computed::FontWeight {
- match *self {
- AbsoluteFontWeight::Weight(weight) => computed::FontWeight::from_float(weight.get()),
- AbsoluteFontWeight::Normal => computed::FontWeight::NORMAL,
- AbsoluteFontWeight::Bold => computed::FontWeight::BOLD,
- }
- }
-}
-
-impl Parse for AbsoluteFontWeight {
- fn parse<'i, 't>(
- context: &ParserContext,
- input: &mut Parser<'i, 't>,
- ) -> Result<Self, ParseError<'i>> {
- if let Ok(number) = input.try_parse(|input| Number::parse(context, input)) {
- // We could add another AllowedNumericType value, but it doesn't
- // seem worth it just for a single property with such a weird range,
- // so we do the clamping here manually.
- if !number.was_calc() &&
- (number.get() < MIN_FONT_WEIGHT || number.get() > MAX_FONT_WEIGHT)
- {
- return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError));
- }
- return Ok(AbsoluteFontWeight::Weight(number));
- }
-
- Ok(try_match_ident_ignore_ascii_case! { input,
- "normal" => AbsoluteFontWeight::Normal,
- "bold" => AbsoluteFontWeight::Bold,
- })
- }
-}
-
-/// The specified value of the `font-style` property, without the system font
-/// crap.
-pub type SpecifiedFontStyle = generics::FontStyle<Angle>;
-
-impl ToCss for SpecifiedFontStyle {
- fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
- where
- W: Write,
- {
- match *self {
- generics::FontStyle::Normal => dest.write_str("normal"),
- generics::FontStyle::Italic => dest.write_str("italic"),
- generics::FontStyle::Oblique(ref angle) => {
- dest.write_str("oblique")?;
- if *angle != Self::default_angle() {
- dest.write_char(' ')?;
- angle.to_css(dest)?;
- }
- Ok(())
- },
- }
- }
-}
-
-impl Parse for SpecifiedFontStyle {
- fn parse<'i, 't>(
- context: &ParserContext,
- input: &mut Parser<'i, 't>,
- ) -> Result<Self, ParseError<'i>> {
- Ok(try_match_ident_ignore_ascii_case! { input,
- "normal" => generics::FontStyle::Normal,
- "italic" => generics::FontStyle::Italic,
- "oblique" => {
- let angle = input.try_parse(|input| Self::parse_angle(context, input))
- .unwrap_or_else(|_| Self::default_angle());
-
- generics::FontStyle::Oblique(angle)
- },
- })
- }
-}
-
-impl ToComputedValue for SpecifiedFontStyle {
- type ComputedValue = computed::FontStyle;
-
- fn to_computed_value(&self, _: &Context) -> Self::ComputedValue {
- match *self {
- Self::Normal => computed::FontStyle::NORMAL,
- Self::Italic => computed::FontStyle::ITALIC,
- Self::Oblique(ref angle) => computed::FontStyle::oblique(angle.degrees()),
- }
- }
-
- fn from_computed_value(computed: &Self::ComputedValue) -> Self {
- if *computed == computed::FontStyle::NORMAL {
- return Self::Normal;
- }
- if *computed == computed::FontStyle::ITALIC {
- return Self::Italic;
- }
- let degrees = computed.oblique_degrees();
- generics::FontStyle::Oblique(Angle::from_degrees(degrees, /* was_calc = */ false))
- }
-}
-
-/// From https://drafts.csswg.org/css-fonts-4/#valdef-font-style-oblique-angle:
-///
-/// Values less than -90deg or values greater than 90deg are
-/// invalid and are treated as parse errors.
-///
-/// The maximum angle value that `font-style: oblique` should compute to.
-pub const FONT_STYLE_OBLIQUE_MAX_ANGLE_DEGREES: f32 = 90.;
-
-/// The minimum angle value that `font-style: oblique` should compute to.
-pub const FONT_STYLE_OBLIQUE_MIN_ANGLE_DEGREES: f32 = -90.;
-
-impl SpecifiedFontStyle {
- /// Gets a clamped angle in degrees from a specified Angle.
- pub fn compute_angle_degrees(angle: &Angle) -> f32 {
- angle
- .degrees()
- .max(FONT_STYLE_OBLIQUE_MIN_ANGLE_DEGREES)
- .min(FONT_STYLE_OBLIQUE_MAX_ANGLE_DEGREES)
- }
-
- /// Parse a suitable angle for font-style: oblique.
- pub fn parse_angle<'i, 't>(
- context: &ParserContext,
- input: &mut Parser<'i, 't>,
- ) -> Result<Angle, ParseError<'i>> {
- let angle = Angle::parse(context, input)?;
- if angle.was_calc() {
- return Ok(angle);
- }
-
- let degrees = angle.degrees();
- if degrees < FONT_STYLE_OBLIQUE_MIN_ANGLE_DEGREES ||
- degrees > FONT_STYLE_OBLIQUE_MAX_ANGLE_DEGREES
- {
- return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError));
- }
- return Ok(angle);
- }
-
- /// The default angle for `font-style: oblique`.
- pub fn default_angle() -> Angle {
- Angle::from_degrees(
- computed::FontStyle::DEFAULT_OBLIQUE_DEGREES as f32,
- /* was_calc = */ false,
- )
- }
-}
-
-/// The specified value of the `font-style` property.
-#[derive(
- Clone, Copy, Debug, MallocSizeOf, Parse, PartialEq, SpecifiedValueInfo, ToCss, ToShmem,
-)]
-#[allow(missing_docs)]
-pub enum FontStyle {
- Specified(SpecifiedFontStyle),
- #[css(skip)]
- System(SystemFont),
-}
-
-impl FontStyle {
- /// Return the `normal` value.
- #[inline]
- pub fn normal() -> Self {
- FontStyle::Specified(generics::FontStyle::Normal)
- }
-
- system_font_methods!(FontStyle, font_style);
-}
-
-impl ToComputedValue for FontStyle {
- type ComputedValue = computed::FontStyle;
-
- fn to_computed_value(&self, context: &Context) -> Self::ComputedValue {
- match *self {
- FontStyle::Specified(ref specified) => specified.to_computed_value(context),
- FontStyle::System(..) => self.compute_system(context),
- }
- }
-
- fn from_computed_value(computed: &Self::ComputedValue) -> Self {
- FontStyle::Specified(SpecifiedFontStyle::from_computed_value(computed))
- }
-}
-
-/// A value for the `font-stretch` property.
-///
-/// https://drafts.csswg.org/css-fonts-4/#font-stretch-prop
-#[allow(missing_docs)]
-#[derive(
- Clone, Copy, Debug, MallocSizeOf, Parse, PartialEq, SpecifiedValueInfo, ToCss, ToShmem,
-)]
-pub enum FontStretch {
- Stretch(NonNegativePercentage),
- Keyword(FontStretchKeyword),
- #[css(skip)]
- System(SystemFont),
-}
-
-/// A keyword value for `font-stretch`.
-#[derive(
- Clone, Copy, Debug, MallocSizeOf, Parse, PartialEq, SpecifiedValueInfo, ToCss, ToShmem,
-)]
-#[allow(missing_docs)]
-pub enum FontStretchKeyword {
- Normal,
- Condensed,
- UltraCondensed,
- ExtraCondensed,
- SemiCondensed,
- SemiExpanded,
- Expanded,
- ExtraExpanded,
- UltraExpanded,
-}
-
-impl FontStretchKeyword {
- /// Turns the keyword into a computed value.
- pub fn compute(&self) -> computed::FontStretch {
- computed::FontStretch::from_keyword(*self)
- }
-
- /// Does the opposite operation to `compute`, in order to serialize keywords
- /// if possible.
- pub fn from_percentage(p: f32) -> Option<Self> {
- computed::FontStretch::from_percentage(p).as_keyword()
- }
-}
-
-impl FontStretch {
- /// `normal`.
- pub fn normal() -> Self {
- FontStretch::Keyword(FontStretchKeyword::Normal)
- }
-
- system_font_methods!(FontStretch, font_stretch);
-}
-
-impl ToComputedValue for FontStretch {
- type ComputedValue = computed::FontStretch;
-
- fn to_computed_value(&self, context: &Context) -> Self::ComputedValue {
- match *self {
- FontStretch::Stretch(ref percentage) => {
- let percentage = percentage.to_computed_value(context).0;
- computed::FontStretch::from_percentage(percentage.0)
- },
- FontStretch::Keyword(ref kw) => kw.compute(),
- FontStretch::System(_) => self.compute_system(context),
- }
- }
-
- fn from_computed_value(computed: &Self::ComputedValue) -> Self {
- FontStretch::Stretch(NonNegativePercentage::from_computed_value(&NonNegative(
- computed.to_percentage(),
- )))
- }
-}
-
-/// CSS font keywords
-#[derive(
- Animate,
- Clone,
- ComputeSquaredDistance,
- Copy,
- Debug,
- MallocSizeOf,
- Parse,
- PartialEq,
- SpecifiedValueInfo,
- ToAnimatedValue,
- ToAnimatedZero,
- ToComputedValue,
- ToCss,
- ToResolvedValue,
- ToShmem,
- Serialize,
- Deserialize,
-)]
-#[allow(missing_docs)]
-#[repr(u8)]
-pub enum FontSizeKeyword {
- #[css(keyword = "xx-small")]
- XXSmall,
- XSmall,
- Small,
- Medium,
- Large,
- XLarge,
- #[css(keyword = "xx-large")]
- XXLarge,
- #[css(keyword = "xxx-large")]
- XXXLarge,
- #[css(skip)]
- None,
-}
-
-impl FontSizeKeyword {
- /// Convert to an HTML <font size> value
- #[inline]
- pub fn html_size(self) -> u8 {
- self as u8
- }
-}
-
-impl Default for FontSizeKeyword {
- fn default() -> Self {
- FontSizeKeyword::Medium
- }
-}
-
-#[derive(
- Animate,
- Clone,
- ComputeSquaredDistance,
- Copy,
- Debug,
- MallocSizeOf,
- PartialEq,
- ToAnimatedValue,
- ToAnimatedZero,
- ToComputedValue,
- ToCss,
- ToResolvedValue,
- ToShmem,
-)]
-#[cfg_attr(feature = "servo", derive(Serialize, Deserialize))]
-/// Additional information for keyword-derived font sizes.
-pub struct KeywordInfo {
- /// The keyword used
- pub kw: FontSizeKeyword,
- /// A factor to be multiplied by the computed size of the keyword
- #[css(skip)]
- pub factor: f32,
- /// An additional fixed offset to add to the kw * factor in the case of
- /// `calc()`.
- #[css(skip)]
- pub offset: CSSPixelLength,
-}
-
-impl KeywordInfo {
- /// KeywordInfo value for font-size: medium
- pub fn medium() -> Self {
- Self::new(FontSizeKeyword::Medium)
- }
-
- /// KeywordInfo value for font-size: none
- pub fn none() -> Self {
- Self::new(FontSizeKeyword::None)
- }
-
- fn new(kw: FontSizeKeyword) -> Self {
- KeywordInfo {
- kw,
- factor: 1.,
- offset: CSSPixelLength::new(0.),
- }
- }
-
- /// Computes the final size for this font-size keyword, accounting for
- /// text-zoom.
- fn to_computed_value(&self, context: &Context) -> CSSPixelLength {
- debug_assert_ne!(self.kw, FontSizeKeyword::None);
- let base = context.maybe_zoom_text(self.kw.to_length(context).0);
- base * self.factor + context.maybe_zoom_text(self.offset)
- }
-
- /// Given a parent keyword info (self), apply an additional factor/offset to
- /// it.
- fn compose(self, factor: f32) -> Self {
- if self.kw == FontSizeKeyword::None {
- return self;
- }
- KeywordInfo {
- kw: self.kw,
- factor: self.factor * factor,
- offset: self.offset * factor,
- }
- }
-}
-
-impl SpecifiedValueInfo for KeywordInfo {
- fn collect_completion_keywords(f: KeywordsCollectFn) {
- <FontSizeKeyword as SpecifiedValueInfo>::collect_completion_keywords(f);
- }
-}
-
-#[derive(Clone, Debug, MallocSizeOf, PartialEq, SpecifiedValueInfo, ToCss, ToShmem)]
-/// A specified font-size value
-pub enum FontSize {
- /// A length; e.g. 10px.
- Length(LengthPercentage),
- /// A keyword value, along with a ratio and absolute offset.
- /// The ratio in any specified keyword value
- /// will be 1 (with offset 0), but we cascade keywordness even
- /// after font-relative (percent and em) values
- /// have been applied, which is where the ratio
- /// comes in. The offset comes in if we cascaded a calc value,
- /// where the font-relative portion (em and percentage) will
- /// go into the ratio, and the remaining units all computed together
- /// will go into the offset.
- /// See bug 1355707.
- Keyword(KeywordInfo),
- /// font-size: smaller
- Smaller,
- /// font-size: larger
- Larger,
- /// Derived from a specified system font.
- #[css(skip)]
- System(SystemFont),
-}
-
-/// Specifies a prioritized list of font family names or generic family names.
-#[derive(Clone, Debug, Eq, PartialEq, ToCss, ToShmem)]
-#[cfg_attr(feature = "servo", derive(Hash))]
-pub enum FontFamily {
- /// List of `font-family`
- #[css(comma)]
- Values(#[css(iterable)] FontFamilyList),
- /// System font
- #[css(skip)]
- System(SystemFont),
-}
-
-impl FontFamily {
- system_font_methods!(FontFamily, font_family);
-}
-
-impl ToComputedValue for FontFamily {
- type ComputedValue = computed::FontFamily;
-
- fn to_computed_value(&self, context: &Context) -> Self::ComputedValue {
- match *self {
- FontFamily::Values(ref list) => computed::FontFamily {
- families: list.clone(),
- is_system_font: false,
- is_initial: false,
- },
- FontFamily::System(_) => self.compute_system(context),
- }
- }
-
- fn from_computed_value(other: &computed::FontFamily) -> Self {
- FontFamily::Values(other.families.clone())
- }
-}
-
-#[cfg(feature = "gecko")]
-impl MallocSizeOf for FontFamily {
- fn size_of(&self, ops: &mut MallocSizeOfOps) -> usize {
- match *self {
- FontFamily::Values(ref v) => {
- // Although the family list is refcounted, we always attribute
- // its size to the specified value.
- v.list.unconditional_size_of(ops)
- },
- FontFamily::System(_) => 0,
- }
- }
-}
-
-impl Parse for FontFamily {
- /// <family-name>#
- /// <family-name> = <string> | [ <ident>+ ]
- /// TODO: <generic-family>
- fn parse<'i, 't>(
- context: &ParserContext,
- input: &mut Parser<'i, 't>,
- ) -> Result<FontFamily, ParseError<'i>> {
- let values =
- input.parse_comma_separated(|input| SingleFontFamily::parse(context, input))?;
- Ok(FontFamily::Values(FontFamilyList {
- #[cfg(feature = "gecko")]
- list: crate::ArcSlice::from_iter(values.into_iter()),
- #[cfg(feature = "servo")]
- list: values.into_boxed_slice(),
- }))
- }
-}
-
-impl SpecifiedValueInfo for FontFamily {}
-
-/// `FamilyName::parse` is based on `SingleFontFamily::parse` and not the other
-/// way around because we want the former to exclude generic family keywords.
-impl Parse for FamilyName {
- fn parse<'i, 't>(
- context: &ParserContext,
- input: &mut Parser<'i, 't>,
- ) -> Result<Self, ParseError<'i>> {
- match SingleFontFamily::parse(context, input) {
- Ok(SingleFontFamily::FamilyName(name)) => Ok(name),
- Ok(SingleFontFamily::Generic(_)) => {
- Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError))
- },
- Err(e) => Err(e),
- }
- }
-}
-
-/// Preserve the readability of text when font fallback occurs
-pub type FontSizeAdjust = GenericFontSizeAdjust<NonNegativeNumber>;
-
-impl Parse for FontSizeAdjust {
- fn parse<'i, 't>(
- context: &ParserContext,
- input: &mut Parser<'i, 't>,
- ) -> Result<Self, ParseError<'i>> {
- let location = input.current_source_location();
- if let Ok(ident) = input.try_parse(|i| i.expect_ident_cloned()) {
- let basis_enabled = static_prefs::pref!("layout.css.font-size-adjust.basis.enabled");
- let basis = match_ignore_ascii_case! { &ident,
- "none" => return Ok(Self::None),
- // Check for size adjustment basis keywords if enabled.
- "ex-height" if basis_enabled => Self::ExHeight,
- "cap-height" if basis_enabled => Self::CapHeight,
- "ch-width" if basis_enabled => Self::ChWidth,
- "ic-width" if basis_enabled => Self::IcWidth,
- "ic-height" if basis_enabled => Self::IcHeight,
- // Unknown (or disabled) keyword.
- _ => return Err(location.new_custom_error(
- SelectorParseErrorKind::UnexpectedIdent(ident)
- )),
- };
- let value = NonNegativeNumber::parse(context, input)?;
- return Ok(basis(value));
- }
- // Without a basis keyword, the number refers to the 'ex-height' metric.
- let value = NonNegativeNumber::parse(context, input)?;
- Ok(Self::ExHeight(value))
- }
-}
-
-/// This is the ratio applied for font-size: larger
-/// and smaller by both Firefox and Chrome
-const LARGER_FONT_SIZE_RATIO: f32 = 1.2;
-
-/// The default font size.
-pub const FONT_MEDIUM_PX: f32 = 16.0;
-
-impl FontSizeKeyword {
- #[inline]
- #[cfg(feature = "servo")]
- fn to_length(&self, _: &Context) -> NonNegativeLength {
- let medium = Length::new(FONT_MEDIUM_PX);
- // https://drafts.csswg.org/css-fonts-3/#font-size-prop
- NonNegative(match *self {
- FontSizeKeyword::XXSmall => medium * 3.0 / 5.0,
- FontSizeKeyword::XSmall => medium * 3.0 / 4.0,
- FontSizeKeyword::Small => medium * 8.0 / 9.0,
- FontSizeKeyword::Medium => medium,
- FontSizeKeyword::Large => medium * 6.0 / 5.0,
- FontSizeKeyword::XLarge => medium * 3.0 / 2.0,
- FontSizeKeyword::XXLarge => medium * 2.0,
- FontSizeKeyword::XXXLarge => medium * 3.0,
- FontSizeKeyword::None => unreachable!(),
- })
- }
-
- #[cfg(feature = "gecko")]
- #[inline]
- fn to_length(&self, cx: &Context) -> NonNegativeLength {
- let font = cx.style().get_font();
- let family = &font.mFont.family.families;
- let generic = family
- .single_generic()
- .unwrap_or(computed::GenericFontFamily::None);
- let base_size = unsafe {
- Atom::with(font.mLanguage.mRawPtr, |language| {
- cx.device().base_size_for_generic(language, generic)
- })
- };
- self.to_length_without_context(cx.quirks_mode, base_size)
- }
-
- /// Resolve a keyword length without any context, with explicit arguments.
- #[cfg(feature = "gecko")]
- #[inline]
- pub fn to_length_without_context(
- &self,
- quirks_mode: QuirksMode,
- base_size: Length,
- ) -> NonNegativeLength {
- // The tables in this function are originally from
- // nsRuleNode::CalcFontPointSize in Gecko:
- //
- // https://searchfox.org/mozilla-central/rev/c05d9d61188d32b8/layout/style/nsRuleNode.cpp#3150
- //
- // Mapping from base size and HTML size to pixels
- // The first index is (base_size - 9), the second is the
- // HTML size. "0" is CSS keyword xx-small, not HTML size 0,
- // since HTML size 0 is the same as 1.
- //
- // xxs xs s m l xl xxl -
- // - 0/1 2 3 4 5 6 7
- static FONT_SIZE_MAPPING: [[i32; 8]; 8] = [
- [9, 9, 9, 9, 11, 14, 18, 27],
- [9, 9, 9, 10, 12, 15, 20, 30],
- [9, 9, 10, 11, 13, 17, 22, 33],
- [9, 9, 10, 12, 14, 18, 24, 36],
- [9, 10, 12, 13, 16, 20, 26, 39],
- [9, 10, 12, 14, 17, 21, 28, 42],
- [9, 10, 13, 15, 18, 23, 30, 45],
- [9, 10, 13, 16, 18, 24, 32, 48],
- ];
-
- // This table gives us compatibility with WinNav4 for the default fonts only.
- // In WinNav4, the default fonts were:
- //
- // Times/12pt == Times/16px at 96ppi
- // Courier/10pt == Courier/13px at 96ppi
- //
- // xxs xs s m l xl xxl -
- // - 1 2 3 4 5 6 7
- static QUIRKS_FONT_SIZE_MAPPING: [[i32; 8]; 8] = [
- [9, 9, 9, 9, 11, 14, 18, 28],
- [9, 9, 9, 10, 12, 15, 20, 31],
- [9, 9, 9, 11, 13, 17, 22, 34],
- [9, 9, 10, 12, 14, 18, 24, 37],
- [9, 9, 10, 13, 16, 20, 26, 40],
- [9, 9, 11, 14, 17, 21, 28, 42],
- [9, 10, 12, 15, 17, 23, 30, 45],
- [9, 10, 13, 16, 18, 24, 32, 48],
- ];
-
- static FONT_SIZE_FACTORS: [i32; 8] = [60, 75, 89, 100, 120, 150, 200, 300];
- let base_size_px = base_size.px().round() as i32;
- let html_size = self.html_size() as usize;
- NonNegative(if base_size_px >= 9 && base_size_px <= 16 {
- let mapping = if quirks_mode == QuirksMode::Quirks {
- QUIRKS_FONT_SIZE_MAPPING
- } else {
- FONT_SIZE_MAPPING
- };
- Length::new(mapping[(base_size_px - 9) as usize][html_size] as f32)
- } else {
- base_size * FONT_SIZE_FACTORS[html_size] as f32 / 100.0
- })
- }
-}
-
-impl FontSize {
- /// <https://html.spec.whatwg.org/multipage/#rules-for-parsing-a-legacy-font-size>
- pub fn from_html_size(size: u8) -> Self {
- FontSize::Keyword(KeywordInfo::new(match size {
- // If value is less than 1, let it be 1.
- 0 | 1 => FontSizeKeyword::XSmall,
- 2 => FontSizeKeyword::Small,
- 3 => FontSizeKeyword::Medium,
- 4 => FontSizeKeyword::Large,
- 5 => FontSizeKeyword::XLarge,
- 6 => FontSizeKeyword::XXLarge,
- // If value is greater than 7, let it be 7.
- _ => FontSizeKeyword::XXXLarge,
- }))
- }
-
- /// Compute it against a given base font size
- pub fn to_computed_value_against(
- &self,
- context: &Context,
- base_size: FontBaseSize,
- ) -> computed::FontSize {
- use crate::values::specified::length::FontRelativeLength;
-
- let compose_keyword = |factor| {
- context
- .style()
- .get_parent_font()
- .clone_font_size()
- .keyword_info
- .compose(factor)
- };
- let mut info = KeywordInfo::none();
- let size = match *self {
- FontSize::Length(LengthPercentage::Length(ref l)) => {
- if let NoCalcLength::FontRelative(ref value) = *l {
- if let FontRelativeLength::Em(em) = *value {
- // If the parent font was keyword-derived, this is
- // too. Tack the em unit onto the factor
- info = compose_keyword(em);
- }
- }
- let result = l.to_computed_value_with_base_size(context, base_size);
- if l.should_zoom_text() {
- context.maybe_zoom_text(result)
- } else {
- result
- }
- },
- FontSize::Length(LengthPercentage::Percentage(pc)) => {
- // If the parent font was keyword-derived, this is too.
- // Tack the % onto the factor
- info = compose_keyword(pc.0);
- (base_size.resolve(context).computed_size() * pc.0).normalized()
- },
- FontSize::Length(LengthPercentage::Calc(ref calc)) => {
- let calc = calc.to_computed_value_zoomed(context, base_size);
- calc.resolve(base_size.resolve(context).computed_size())
- },
- FontSize::Keyword(i) => {
- // As a specified keyword, this is keyword derived
- info = i;
- i.to_computed_value(context).clamp_to_non_negative()
- },
- FontSize::Smaller => {
- info = compose_keyword(1. / LARGER_FONT_SIZE_RATIO);
- FontRelativeLength::Em(1. / LARGER_FONT_SIZE_RATIO)
- .to_computed_value(context, base_size)
- },
- FontSize::Larger => {
- info = compose_keyword(LARGER_FONT_SIZE_RATIO);
- FontRelativeLength::Em(LARGER_FONT_SIZE_RATIO).to_computed_value(context, base_size)
- },
-
- FontSize::System(_) => {
- #[cfg(feature = "servo")]
- {
- unreachable!()
- }
- #[cfg(feature = "gecko")]
- {
- context
- .cached_system_font
- .as_ref()
- .unwrap()
- .font_size
- .computed_size()
- }
- },
- };
- computed::FontSize {
- computed_size: NonNegative(size),
- used_size: NonNegative(size),
- keyword_info: info,
- }
- }
-}
-
-impl ToComputedValue for FontSize {
- type ComputedValue = computed::FontSize;
-
- #[inline]
- fn to_computed_value(&self, context: &Context) -> computed::FontSize {
- self.to_computed_value_against(context, FontBaseSize::InheritedStyle)
- }
-
- #[inline]
- fn from_computed_value(computed: &computed::FontSize) -> Self {
- FontSize::Length(LengthPercentage::Length(
- ToComputedValue::from_computed_value(&computed.computed_size()),
- ))
- }
-}
-
-impl FontSize {
- system_font_methods!(FontSize);
-
- /// Get initial value for specified font size.
- #[inline]
- pub fn medium() -> Self {
- FontSize::Keyword(KeywordInfo::medium())
- }
-
- /// Parses a font-size, with quirks.
- pub fn parse_quirky<'i, 't>(
- context: &ParserContext,
- input: &mut Parser<'i, 't>,
- allow_quirks: AllowQuirks,
- ) -> Result<FontSize, ParseError<'i>> {
- if let Ok(lp) = input
- .try_parse(|i| LengthPercentage::parse_non_negative_quirky(context, i, allow_quirks))
- {
- return Ok(FontSize::Length(lp));
- }
-
- if let Ok(kw) = input.try_parse(FontSizeKeyword::parse) {
- return Ok(FontSize::Keyword(KeywordInfo::new(kw)));
- }
-
- try_match_ident_ignore_ascii_case! { input,
- "smaller" => Ok(FontSize::Smaller),
- "larger" => Ok(FontSize::Larger),
- }
- }
-}
-
-impl Parse for FontSize {
- /// <length> | <percentage> | <absolute-size> | <relative-size>
- fn parse<'i, 't>(
- context: &ParserContext,
- input: &mut Parser<'i, 't>,
- ) -> Result<FontSize, ParseError<'i>> {
- FontSize::parse_quirky(context, input, AllowQuirks::No)
- }
-}
-
-bitflags! {
- #[cfg_attr(feature = "servo", derive(MallocSizeOf))]
- /// Flags of variant alternates in bit
- struct VariantAlternatesParsingFlags: u8 {
- /// None of variant alternates enabled
- const NORMAL = 0;
- /// Historical forms
- const HISTORICAL_FORMS = 0x01;
- /// Stylistic Alternates
- const STYLISTIC = 0x02;
- /// Stylistic Sets
- const STYLESET = 0x04;
- /// Character Variant
- const CHARACTER_VARIANT = 0x08;
- /// Swash glyphs
- const SWASH = 0x10;
- /// Ornaments glyphs
- const ORNAMENTS = 0x20;
- /// Annotation forms
- const ANNOTATION = 0x40;
- }
-}
-
-#[derive(
- Clone,
- Debug,
- MallocSizeOf,
- PartialEq,
- SpecifiedValueInfo,
- ToCss,
- ToComputedValue,
- ToResolvedValue,
- ToShmem,
-)]
-#[repr(C, u8)]
-/// Set of variant alternates
-pub enum VariantAlternates {
- /// Enables display of stylistic alternates
- #[css(function)]
- Stylistic(CustomIdent),
- /// Enables display with stylistic sets
- #[css(comma, function)]
- Styleset(#[css(iterable)] crate::OwnedSlice<CustomIdent>),
- /// Enables display of specific character variants
- #[css(comma, function)]
- CharacterVariant(#[css(iterable)] crate::OwnedSlice<CustomIdent>),
- /// Enables display of swash glyphs
- #[css(function)]
- Swash(CustomIdent),
- /// Enables replacement of default glyphs with ornaments
- #[css(function)]
- Ornaments(CustomIdent),
- /// Enables display of alternate annotation forms
- #[css(function)]
- Annotation(CustomIdent),
- /// Enables display of historical forms
- HistoricalForms,
-}
-
-#[derive(
- Clone,
- Debug,
- Default,
- MallocSizeOf,
- PartialEq,
- SpecifiedValueInfo,
- ToComputedValue,
- ToCss,
- ToResolvedValue,
- ToShmem,
-)]
-#[repr(transparent)]
-/// List of Variant Alternates
-pub struct FontVariantAlternates(
- #[css(if_empty = "normal", iterable)] crate::OwnedSlice<VariantAlternates>,
-);
-
-impl FontVariantAlternates {
- /// Returns the length of all variant alternates.
- pub fn len(&self) -> usize {
- self.0.iter().fold(0, |acc, alternate| match *alternate {
- VariantAlternates::Swash(_) |
- VariantAlternates::Stylistic(_) |
- VariantAlternates::Ornaments(_) |
- VariantAlternates::Annotation(_) => acc + 1,
- VariantAlternates::Styleset(ref slice) |
- VariantAlternates::CharacterVariant(ref slice) => acc + slice.len(),
- _ => acc,
- })
- }
-}
-
-impl FontVariantAlternates {
- #[inline]
- /// Get initial specified value with VariantAlternatesList
- pub fn get_initial_specified_value() -> Self {
- Default::default()
- }
-}
-
-impl Parse for FontVariantAlternates {
- /// normal |
- /// [ stylistic(<feature-value-name>) ||
- /// historical-forms ||
- /// styleset(<feature-value-name> #) ||
- /// character-variant(<feature-value-name> #) ||
- /// swash(<feature-value-name>) ||
- /// ornaments(<feature-value-name>) ||
- /// annotation(<feature-value-name>) ]
- fn parse<'i, 't>(
- _: &ParserContext,
- input: &mut Parser<'i, 't>,
- ) -> Result<FontVariantAlternates, ParseError<'i>> {
- if input
- .try_parse(|input| input.expect_ident_matching("normal"))
- .is_ok()
- {
- return Ok(Default::default());
- }
-
- let mut stylistic = None;
- let mut historical = None;
- let mut styleset = None;
- let mut character_variant = None;
- let mut swash = None;
- let mut ornaments = None;
- let mut annotation = None;
-
- // Parse values for the various alternate types in any order.
- let mut parsed_alternates = VariantAlternatesParsingFlags::empty();
- macro_rules! check_if_parsed(
- ($input:expr, $flag:path) => (
- if parsed_alternates.contains($flag) {
- return Err($input.new_custom_error(StyleParseErrorKind::UnspecifiedError))
- }
- parsed_alternates |= $flag;
- )
- );
- while let Ok(_) = input.try_parse(|input| match *input.next()? {
- Token::Ident(ref value) if value.eq_ignore_ascii_case("historical-forms") => {
- check_if_parsed!(input, VariantAlternatesParsingFlags::HISTORICAL_FORMS);
- historical = Some(VariantAlternates::HistoricalForms);
- Ok(())
- },
- Token::Function(ref name) => {
- let name = name.clone();
- input.parse_nested_block(|i| {
- match_ignore_ascii_case! { &name,
- "swash" => {
- check_if_parsed!(i, VariantAlternatesParsingFlags::SWASH);
- let location = i.current_source_location();
- let ident = CustomIdent::from_ident(location, i.expect_ident()?, &[])?;
- swash = Some(VariantAlternates::Swash(ident));
- Ok(())
- },
- "stylistic" => {
- check_if_parsed!(i, VariantAlternatesParsingFlags::STYLISTIC);
- let location = i.current_source_location();
- let ident = CustomIdent::from_ident(location, i.expect_ident()?, &[])?;
- stylistic = Some(VariantAlternates::Stylistic(ident));
- Ok(())
- },
- "ornaments" => {
- check_if_parsed!(i, VariantAlternatesParsingFlags::ORNAMENTS);
- let location = i.current_source_location();
- let ident = CustomIdent::from_ident(location, i.expect_ident()?, &[])?;
- ornaments = Some(VariantAlternates::Ornaments(ident));
- Ok(())
- },
- "annotation" => {
- check_if_parsed!(i, VariantAlternatesParsingFlags::ANNOTATION);
- let location = i.current_source_location();
- let ident = CustomIdent::from_ident(location, i.expect_ident()?, &[])?;
- annotation = Some(VariantAlternates::Annotation(ident));
- Ok(())
- },
- "styleset" => {
- check_if_parsed!(i, VariantAlternatesParsingFlags::STYLESET);
- let idents = i.parse_comma_separated(|i| {
- let location = i.current_source_location();
- CustomIdent::from_ident(location, i.expect_ident()?, &[])
- })?;
- styleset = Some(VariantAlternates::Styleset(idents.into()));
- Ok(())
- },
- "character-variant" => {
- check_if_parsed!(i, VariantAlternatesParsingFlags::CHARACTER_VARIANT);
- let idents = i.parse_comma_separated(|i| {
- let location = i.current_source_location();
- CustomIdent::from_ident(location, i.expect_ident()?, &[])
- })?;
- character_variant = Some(VariantAlternates::CharacterVariant(idents.into()));
- Ok(())
- },
- _ => return Err(i.new_custom_error(StyleParseErrorKind::UnspecifiedError)),
- }
- })
- },
- _ => Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError)),
- }) {}
-
- if parsed_alternates.is_empty() {
- return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError));
- }
-
- // Collect the parsed values in canonical order, so that we'll serialize correctly.
- let mut alternates = Vec::new();
- macro_rules! push_if_some(
- ($value:expr) => (
- if let Some(v) = $value {
- alternates.push(v);
- }
- )
- );
- push_if_some!(stylistic);
- push_if_some!(historical);
- push_if_some!(styleset);
- push_if_some!(character_variant);
- push_if_some!(swash);
- push_if_some!(ornaments);
- push_if_some!(annotation);
-
- Ok(FontVariantAlternates(alternates.into()))
- }
-}
-
-macro_rules! impl_variant_east_asian {
- {
- $(
- $(#[$($meta:tt)+])*
- $ident:ident / $css:expr => $gecko:ident = $value:expr,
- )+
- } => {
- bitflags! {
- #[derive(MallocSizeOf, ToComputedValue, ToResolvedValue, ToShmem)]
- /// Vairants for east asian variant
- pub struct FontVariantEastAsian: u16 {
- /// None of the features
- const NORMAL = 0;
- $(
- $(#[$($meta)+])*
- const $ident = $value;
- )+
- }
- }
-
- impl ToCss for FontVariantEastAsian {
- fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
- where
- W: Write,
- {
- if self.is_empty() {
- return dest.write_str("normal");
- }
-
- let mut writer = SequenceWriter::new(dest, " ");
- $(
- if self.intersects(Self::$ident) {
- writer.raw_item($css)?;
- }
- )+
- Ok(())
- }
- }
-
- /// Asserts that all variant-east-asian matches its NS_FONT_VARIANT_EAST_ASIAN_* value.
- #[cfg(feature = "gecko")]
- #[inline]
- pub fn assert_variant_east_asian_matches() {
- use crate::gecko_bindings::structs;
- $(
- debug_assert_eq!(structs::$gecko as u16, FontVariantEastAsian::$ident.bits());
- )+
- }
-
- impl SpecifiedValueInfo for FontVariantEastAsian {
- fn collect_completion_keywords(f: KeywordsCollectFn) {
- f(&["normal", $($css,)+]);
- }
- }
- }
-}
-
-impl_variant_east_asian! {
- /// Enables rendering of JIS78 forms (OpenType feature: jp78)
- JIS78 / "jis78" => NS_FONT_VARIANT_EAST_ASIAN_JIS78 = 0x01,
- /// Enables rendering of JIS83 forms (OpenType feature: jp83).
- JIS83 / "jis83" => NS_FONT_VARIANT_EAST_ASIAN_JIS83 = 0x02,
- /// Enables rendering of JIS90 forms (OpenType feature: jp90).
- JIS90 / "jis90" => NS_FONT_VARIANT_EAST_ASIAN_JIS90 = 0x04,
- /// Enables rendering of JIS2004 forms (OpenType feature: jp04).
- JIS04 / "jis04" => NS_FONT_VARIANT_EAST_ASIAN_JIS04 = 0x08,
- /// Enables rendering of simplified forms (OpenType feature: smpl).
- SIMPLIFIED / "simplified" => NS_FONT_VARIANT_EAST_ASIAN_SIMPLIFIED = 0x10,
- /// Enables rendering of traditional forms (OpenType feature: trad).
- TRADITIONAL / "traditional" => NS_FONT_VARIANT_EAST_ASIAN_TRADITIONAL = 0x20,
- /// Enables rendering of full-width variants (OpenType feature: fwid).
- FULL_WIDTH / "full-width" => NS_FONT_VARIANT_EAST_ASIAN_FULL_WIDTH = 0x40,
- /// Enables rendering of proportionally-spaced variants (OpenType feature: pwid).
- PROPORTIONAL_WIDTH / "proportional-width" => NS_FONT_VARIANT_EAST_ASIAN_PROP_WIDTH = 0x80,
- /// Enables display of ruby variant glyphs (OpenType feature: ruby).
- RUBY / "ruby" => NS_FONT_VARIANT_EAST_ASIAN_RUBY = 0x100,
-}
-
-#[cfg(feature = "gecko")]
-impl FontVariantEastAsian {
- /// Obtain a specified value from a Gecko keyword value
- ///
- /// Intended for use with presentation attributes, not style structs
- pub fn from_gecko_keyword(kw: u16) -> Self {
- Self::from_bits_truncate(kw)
- }
-
- /// Transform into gecko keyword
- pub fn to_gecko_keyword(self) -> u16 {
- self.bits()
- }
-}
-
-#[cfg(feature = "gecko")]
-impl_gecko_keyword_conversions!(FontVariantEastAsian, u16);
-
-impl Parse for FontVariantEastAsian {
- /// normal | [ <east-asian-variant-values> || <east-asian-width-values> || ruby ]
- /// <east-asian-variant-values> = [ jis78 | jis83 | jis90 | jis04 | simplified | traditional ]
- /// <east-asian-width-values> = [ full-width | proportional-width ]
- fn parse<'i, 't>(
- _context: &ParserContext,
- input: &mut Parser<'i, 't>,
- ) -> Result<Self, ParseError<'i>> {
- let mut result = Self::empty();
-
- if input
- .try_parse(|input| input.expect_ident_matching("normal"))
- .is_ok()
- {
- return Ok(result);
- }
-
- while let Ok(flag) = input.try_parse(|input| {
- Ok(
- match_ignore_ascii_case! { &input.expect_ident().map_err(|_| ())?,
- "jis78" =>
- exclusive_value!((result, Self::JIS78 | Self::JIS83 |
- Self::JIS90 | Self::JIS04 |
- Self::SIMPLIFIED | Self::TRADITIONAL
- ) => Self::JIS78),
- "jis83" =>
- exclusive_value!((result, Self::JIS78 | Self::JIS83 |
- Self::JIS90 | Self::JIS04 |
- Self::SIMPLIFIED | Self::TRADITIONAL
- ) => Self::JIS83),
- "jis90" =>
- exclusive_value!((result, Self::JIS78 | Self::JIS83 |
- Self::JIS90 | Self::JIS04 |
- Self::SIMPLIFIED | Self::TRADITIONAL
- ) => Self::JIS90),
- "jis04" =>
- exclusive_value!((result, Self::JIS78 | Self::JIS83 |
- Self::JIS90 | Self::JIS04 |
- Self::SIMPLIFIED | Self::TRADITIONAL
- ) => Self::JIS04),
- "simplified" =>
- exclusive_value!((result, Self::JIS78 | Self::JIS83 |
- Self::JIS90 | Self::JIS04 |
- Self::SIMPLIFIED | Self::TRADITIONAL
- ) => Self::SIMPLIFIED),
- "traditional" =>
- exclusive_value!((result, Self::JIS78 | Self::JIS83 |
- Self::JIS90 | Self::JIS04 |
- Self::SIMPLIFIED | Self::TRADITIONAL
- ) => Self::TRADITIONAL),
- "full-width" =>
- exclusive_value!((result, Self::FULL_WIDTH |
- Self::PROPORTIONAL_WIDTH
- ) => Self::FULL_WIDTH),
- "proportional-width" =>
- exclusive_value!((result, Self::FULL_WIDTH |
- Self::PROPORTIONAL_WIDTH
- ) => Self::PROPORTIONAL_WIDTH),
- "ruby" =>
- exclusive_value!((result, Self::RUBY) => Self::RUBY),
- _ => return Err(()),
- },
- )
- }) {
- result.insert(flag);
- }
-
- if !result.is_empty() {
- Ok(result)
- } else {
- Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError))
- }
- }
-}
-
-macro_rules! impl_variant_ligatures {
- {
- $(
- $(#[$($meta:tt)+])*
- $ident:ident / $css:expr => $gecko:ident = $value:expr,
- )+
- } => {
- bitflags! {
- #[derive(MallocSizeOf, ToComputedValue, ToResolvedValue, ToShmem)]
- /// Variants of ligatures
- pub struct FontVariantLigatures: u16 {
- /// Specifies that common default features are enabled
- const NORMAL = 0;
- $(
- $(#[$($meta)+])*
- const $ident = $value;
- )+
- }
- }
-
- impl ToCss for FontVariantLigatures {
- fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
- where
- W: Write,
- {
- if self.is_empty() {
- return dest.write_str("normal");
- }
- if self.contains(FontVariantLigatures::NONE) {
- return dest.write_str("none");
- }
-
- let mut writer = SequenceWriter::new(dest, " ");
- $(
- if self.intersects(FontVariantLigatures::$ident) {
- writer.raw_item($css)?;
- }
- )+
- Ok(())
- }
- }
-
- /// Asserts that all variant-east-asian matches its NS_FONT_VARIANT_EAST_ASIAN_* value.
- #[cfg(feature = "gecko")]
- #[inline]
- pub fn assert_variant_ligatures_matches() {
- use crate::gecko_bindings::structs;
- $(
- debug_assert_eq!(structs::$gecko as u16, FontVariantLigatures::$ident.bits());
- )+
- }
-
- impl SpecifiedValueInfo for FontVariantLigatures {
- fn collect_completion_keywords(f: KeywordsCollectFn) {
- f(&["normal", $($css,)+]);
- }
- }
- }
-}
-
-impl_variant_ligatures! {
- /// Specifies that all types of ligatures and contextual forms
- /// covered by this property are explicitly disabled
- NONE / "none" => NS_FONT_VARIANT_LIGATURES_NONE = 0x01,
- /// Enables display of common ligatures
- COMMON_LIGATURES / "common-ligatures" => NS_FONT_VARIANT_LIGATURES_COMMON = 0x02,
- /// Disables display of common ligatures
- NO_COMMON_LIGATURES / "no-common-ligatures" => NS_FONT_VARIANT_LIGATURES_NO_COMMON = 0x04,
- /// Enables display of discretionary ligatures
- DISCRETIONARY_LIGATURES / "discretionary-ligatures" => NS_FONT_VARIANT_LIGATURES_DISCRETIONARY = 0x08,
- /// Disables display of discretionary ligatures
- NO_DISCRETIONARY_LIGATURES / "no-discretionary-ligatures" => NS_FONT_VARIANT_LIGATURES_NO_DISCRETIONARY = 0x10,
- /// Enables display of historical ligatures
- HISTORICAL_LIGATURES / "historical-ligatures" => NS_FONT_VARIANT_LIGATURES_HISTORICAL = 0x20,
- /// Disables display of historical ligatures
- NO_HISTORICAL_LIGATURES / "no-historical-ligatures" => NS_FONT_VARIANT_LIGATURES_NO_HISTORICAL = 0x40,
- /// Enables display of contextual alternates
- CONTEXTUAL / "contextual" => NS_FONT_VARIANT_LIGATURES_CONTEXTUAL = 0x80,
- /// Disables display of contextual alternates
- NO_CONTEXTUAL / "no-contextual" => NS_FONT_VARIANT_LIGATURES_NO_CONTEXTUAL = 0x100,
-}
-
-#[cfg(feature = "gecko")]
-impl FontVariantLigatures {
- /// Obtain a specified value from a Gecko keyword value
- ///
- /// Intended for use with presentation attributes, not style structs
- pub fn from_gecko_keyword(kw: u16) -> Self {
- Self::from_bits_truncate(kw)
- }
-
- /// Transform into gecko keyword
- pub fn to_gecko_keyword(self) -> u16 {
- self.bits()
- }
-}
-
-#[cfg(feature = "gecko")]
-impl_gecko_keyword_conversions!(FontVariantLigatures, u16);
-
-impl Parse for FontVariantLigatures {
- /// normal | none |
- /// [ <common-lig-values> ||
- /// <discretionary-lig-values> ||
- /// <historical-lig-values> ||
- /// <contextual-alt-values> ]
- /// <common-lig-values> = [ common-ligatures | no-common-ligatures ]
- /// <discretionary-lig-values> = [ discretionary-ligatures | no-discretionary-ligatures ]
- /// <historical-lig-values> = [ historical-ligatures | no-historical-ligatures ]
- /// <contextual-alt-values> = [ contextual | no-contextual ]
- fn parse<'i, 't>(
- _context: &ParserContext,
- input: &mut Parser<'i, 't>,
- ) -> Result<Self, ParseError<'i>> {
- let mut result = Self::empty();
- if input
- .try_parse(|input| input.expect_ident_matching("normal"))
- .is_ok()
- {
- return Ok(result);
- }
- if input
- .try_parse(|input| input.expect_ident_matching("none"))
- .is_ok()
- {
- return Ok(Self::NONE);
- }
-
- while let Ok(flag) = input.try_parse(|input| {
- Ok(
- match_ignore_ascii_case! { &input.expect_ident().map_err(|_| ())?,
- "common-ligatures" =>
- exclusive_value!((result, Self::COMMON_LIGATURES |
- Self::NO_COMMON_LIGATURES
- ) => Self::COMMON_LIGATURES),
- "no-common-ligatures" =>
- exclusive_value!((result, Self::COMMON_LIGATURES |
- Self::NO_COMMON_LIGATURES
- ) => Self::NO_COMMON_LIGATURES),
- "discretionary-ligatures" =>
- exclusive_value!((result, Self::DISCRETIONARY_LIGATURES |
- Self::NO_DISCRETIONARY_LIGATURES
- ) => Self::DISCRETIONARY_LIGATURES),
- "no-discretionary-ligatures" =>
- exclusive_value!((result, Self::DISCRETIONARY_LIGATURES |
- Self::NO_DISCRETIONARY_LIGATURES
- ) => Self::NO_DISCRETIONARY_LIGATURES),
- "historical-ligatures" =>
- exclusive_value!((result, Self::HISTORICAL_LIGATURES |
- Self::NO_HISTORICAL_LIGATURES
- ) => Self::HISTORICAL_LIGATURES),
- "no-historical-ligatures" =>
- exclusive_value!((result, Self::HISTORICAL_LIGATURES |
- Self::NO_HISTORICAL_LIGATURES
- ) => Self::NO_HISTORICAL_LIGATURES),
- "contextual" =>
- exclusive_value!((result, Self::CONTEXTUAL |
- Self::NO_CONTEXTUAL
- ) => Self::CONTEXTUAL),
- "no-contextual" =>
- exclusive_value!((result, Self::CONTEXTUAL |
- Self::NO_CONTEXTUAL
- ) => Self::NO_CONTEXTUAL),
- _ => return Err(()),
- },
- )
- }) {
- result.insert(flag);
- }
-
- if !result.is_empty() {
- Ok(result)
- } else {
- Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError))
- }
- }
-}
-
-macro_rules! impl_variant_numeric {
- {
- $(
- $(#[$($meta:tt)+])*
- $ident:ident / $css:expr => $gecko:ident = $value:expr,
- )+
- } => {
- bitflags! {
- #[derive(MallocSizeOf, ToComputedValue, ToResolvedValue, ToShmem)]
- /// Variants of numeric values
- pub struct FontVariantNumeric: u8 {
- /// None of other variants are enabled.
- const NORMAL = 0;
- $(
- $(#[$($meta)+])*
- const $ident = $value;
- )+
- }
- }
-
- impl ToCss for FontVariantNumeric {
- fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
- where
- W: Write,
- {
- if self.is_empty() {
- return dest.write_str("normal");
- }
-
- let mut writer = SequenceWriter::new(dest, " ");
- $(
- if self.intersects(FontVariantNumeric::$ident) {
- writer.raw_item($css)?;
- }
- )+
- Ok(())
- }
- }
-
- /// Asserts that all variant-east-asian matches its NS_FONT_VARIANT_EAST_ASIAN_* value.
- #[cfg(feature = "gecko")]
- #[inline]
- pub fn assert_variant_numeric_matches() {
- use crate::gecko_bindings::structs;
- $(
- debug_assert_eq!(structs::$gecko as u8, FontVariantNumeric::$ident.bits());
- )+
- }
-
- impl SpecifiedValueInfo for FontVariantNumeric {
- fn collect_completion_keywords(f: KeywordsCollectFn) {
- f(&["normal", $($css,)+]);
- }
- }
- }
-}
-
-impl_variant_numeric! {
- /// Enables display of lining numerals.
- LINING_NUMS / "lining-nums" => NS_FONT_VARIANT_NUMERIC_LINING = 0x01,
- /// Enables display of old-style numerals.
- OLDSTYLE_NUMS / "oldstyle-nums" => NS_FONT_VARIANT_NUMERIC_OLDSTYLE = 0x02,
- /// Enables display of proportional numerals.
- PROPORTIONAL_NUMS / "proportional-nums" => NS_FONT_VARIANT_NUMERIC_PROPORTIONAL = 0x04,
- /// Enables display of tabular numerals.
- TABULAR_NUMS / "tabular-nums" => NS_FONT_VARIANT_NUMERIC_TABULAR = 0x08,
- /// Enables display of lining diagonal fractions.
- DIAGONAL_FRACTIONS / "diagonal-fractions" => NS_FONT_VARIANT_NUMERIC_DIAGONAL_FRACTIONS = 0x10,
- /// Enables display of lining stacked fractions.
- STACKED_FRACTIONS / "stacked-fractions" => NS_FONT_VARIANT_NUMERIC_STACKED_FRACTIONS = 0x20,
- /// Enables display of letter forms used with ordinal numbers.
- ORDINAL / "ordinal" => NS_FONT_VARIANT_NUMERIC_ORDINAL = 0x80,
- /// Enables display of slashed zeros.
- SLASHED_ZERO / "slashed-zero" => NS_FONT_VARIANT_NUMERIC_SLASHZERO = 0x40,
-}
-
-#[cfg(feature = "gecko")]
-impl FontVariantNumeric {
- /// Obtain a specified value from a Gecko keyword value
- ///
- /// Intended for use with presentation attributes, not style structs
- pub fn from_gecko_keyword(kw: u8) -> Self {
- Self::from_bits_truncate(kw)
- }
-
- /// Transform into gecko keyword
- pub fn to_gecko_keyword(self) -> u8 {
- self.bits()
- }
-}
-
-#[cfg(feature = "gecko")]
-impl_gecko_keyword_conversions!(FontVariantNumeric, u8);
-
-impl Parse for FontVariantNumeric {
- /// normal |
- /// [ <numeric-figure-values> ||
- /// <numeric-spacing-values> ||
- /// <numeric-fraction-values> ||
- /// ordinal ||
- /// slashed-zero ]
- /// <numeric-figure-values> = [ lining-nums | oldstyle-nums ]
- /// <numeric-spacing-values> = [ proportional-nums | tabular-nums ]
- /// <numeric-fraction-values> = [ diagonal-fractions | stacked-fractions ]
- fn parse<'i, 't>(
- _context: &ParserContext,
- input: &mut Parser<'i, 't>,
- ) -> Result<Self, ParseError<'i>> {
- let mut result = Self::empty();
-
- if input
- .try_parse(|input| input.expect_ident_matching("normal"))
- .is_ok()
- {
- return Ok(result);
- }
-
- while let Ok(flag) = input.try_parse(|input| {
- Ok(
- match_ignore_ascii_case! { &input.expect_ident().map_err(|_| ())?,
- "ordinal" =>
- exclusive_value!((result, Self::ORDINAL) => Self::ORDINAL),
- "slashed-zero" =>
- exclusive_value!((result, Self::SLASHED_ZERO) => Self::SLASHED_ZERO),
- "lining-nums" =>
- exclusive_value!((result, Self::LINING_NUMS |
- Self::OLDSTYLE_NUMS
- ) => Self::LINING_NUMS),
- "oldstyle-nums" =>
- exclusive_value!((result, Self::LINING_NUMS |
- Self::OLDSTYLE_NUMS
- ) => Self::OLDSTYLE_NUMS),
- "proportional-nums" =>
- exclusive_value!((result, Self::PROPORTIONAL_NUMS |
- Self::TABULAR_NUMS
- ) => Self::PROPORTIONAL_NUMS),
- "tabular-nums" =>
- exclusive_value!((result, Self::PROPORTIONAL_NUMS |
- Self::TABULAR_NUMS
- ) => Self::TABULAR_NUMS),
- "diagonal-fractions" =>
- exclusive_value!((result, Self::DIAGONAL_FRACTIONS |
- Self::STACKED_FRACTIONS
- ) => Self::DIAGONAL_FRACTIONS),
- "stacked-fractions" =>
- exclusive_value!((result, Self::DIAGONAL_FRACTIONS |
- Self::STACKED_FRACTIONS
- ) => Self::STACKED_FRACTIONS),
- _ => return Err(()),
- },
- )
- }) {
- result.insert(flag);
- }
-
- if !result.is_empty() {
- Ok(result)
- } else {
- Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError))
- }
- }
-}
-
-/// This property provides low-level control over OpenType or TrueType font features.
-pub type FontFeatureSettings = FontSettings<FeatureTagValue<Integer>>;
-
-/// For font-language-override, use the same representation as the computed value.
-pub use crate::values::computed::font::FontLanguageOverride;
-
-impl Parse for FontLanguageOverride {
- /// normal | <string>
- fn parse<'i, 't>(
- _: &ParserContext,
- input: &mut Parser<'i, 't>,
- ) -> Result<FontLanguageOverride, ParseError<'i>> {
- if input
- .try_parse(|input| input.expect_ident_matching("normal"))
- .is_ok()
- {
- return Ok(FontLanguageOverride::normal());
- }
-
- let string = input.expect_string()?;
-
- // The OpenType spec requires tags to be 1 to 4 ASCII characters:
- // https://learn.microsoft.com/en-gb/typography/opentype/spec/otff#data-types
- if string.is_empty() || string.len() > 4 || !string.is_ascii() {
- return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError));
- }
-
- let mut bytes = [b' '; 4];
- for (byte, str_byte) in bytes.iter_mut().zip(string.as_bytes()) {
- *byte = *str_byte;
- }
-
- Ok(FontLanguageOverride(u32::from_be_bytes(bytes)))
- }
-}
-
-/// A value for any of the font-synthesis-{weight,style,small-caps} properties.
-#[repr(u8)]
-#[derive(
- Clone,
- Copy,
- Debug,
- Eq,
- MallocSizeOf,
- Parse,
- PartialEq,
- SpecifiedValueInfo,
- ToComputedValue,
- ToCss,
- ToResolvedValue,
- ToShmem,
-)]
-pub enum FontSynthesis {
- /// This attribute may be synthesized if not supported by a face.
- Auto,
- /// Do not attempt to synthesis this style attribute.
- None,
-}
-
-#[derive(
- Clone,
- Debug,
- Eq,
- MallocSizeOf,
- PartialEq,
- SpecifiedValueInfo,
- ToComputedValue,
- ToResolvedValue,
- ToShmem,
-)]
-#[repr(C)]
-/// Allows authors to choose a palette from those supported by a color font
-/// (and potentially @font-palette-values overrides).
-pub struct FontPalette(Atom);
-
-#[allow(missing_docs)]
-impl FontPalette {
- pub fn normal() -> Self {
- Self(atom!("normal"))
- }
- pub fn light() -> Self {
- Self(atom!("light"))
- }
- pub fn dark() -> Self {
- Self(atom!("dark"))
- }
-}
-
-impl Parse for FontPalette {
- /// normal | light | dark | dashed-ident
- fn parse<'i, 't>(
- _context: &ParserContext,
- input: &mut Parser<'i, 't>,
- ) -> Result<FontPalette, ParseError<'i>> {
- let location = input.current_source_location();
- let ident = input.expect_ident()?;
- match_ignore_ascii_case! { &ident,
- "normal" => Ok(Self::normal()),
- "light" => Ok(Self::light()),
- "dark" => Ok(Self::dark()),
- _ => if ident.starts_with("--") {
- Ok(Self(Atom::from(ident.as_ref())))
- } else {
- Err(location.new_custom_error(SelectorParseErrorKind::UnexpectedIdent(ident.clone())))
- },
- }
- }
-}
-
-impl ToCss for FontPalette {
- fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
- where
- W: Write,
- {
- serialize_atom_identifier(&self.0, dest)
- }
-}
-
-/// This property provides low-level control over OpenType or TrueType font
-/// variations.
-pub type FontVariationSettings = FontSettings<VariationValue<Number>>;
-
-fn parse_one_feature_value<'i, 't>(
- context: &ParserContext,
- input: &mut Parser<'i, 't>,
-) -> Result<Integer, ParseError<'i>> {
- if let Ok(integer) = input.try_parse(|i| Integer::parse_non_negative(context, i)) {
- return Ok(integer);
- }
-
- try_match_ident_ignore_ascii_case! { input,
- "on" => Ok(Integer::new(1)),
- "off" => Ok(Integer::new(0)),
- }
-}
-
-impl Parse for FeatureTagValue<Integer> {
- /// https://drafts.csswg.org/css-fonts-4/#feature-tag-value
- fn parse<'i, 't>(
- context: &ParserContext,
- input: &mut Parser<'i, 't>,
- ) -> Result<Self, ParseError<'i>> {
- let tag = FontTag::parse(context, input)?;
- let value = input
- .try_parse(|i| parse_one_feature_value(context, i))
- .unwrap_or_else(|_| Integer::new(1));
-
- Ok(Self { tag, value })
- }
-}
-
-impl Parse for VariationValue<Number> {
- /// This is the `<string> <number>` part of the font-variation-settings
- /// syntax.
- fn parse<'i, 't>(
- context: &ParserContext,
- input: &mut Parser<'i, 't>,
- ) -> Result<Self, ParseError<'i>> {
- let tag = FontTag::parse(context, input)?;
- let value = Number::parse(context, input)?;
- Ok(Self { tag, value })
- }
-}
-
-/// A metrics override value for a @font-face descriptor
-///
-/// https://drafts.csswg.org/css-fonts/#font-metrics-override-desc
-#[derive(
- Clone, Copy, Debug, MallocSizeOf, Parse, PartialEq, SpecifiedValueInfo, ToCss, ToShmem,
-)]
-pub enum MetricsOverride {
- /// A non-negative `<percentage>` of the computed font size
- Override(NonNegativePercentage),
- /// Normal metrics from the font.
- Normal,
-}
-
-impl MetricsOverride {
- #[inline]
- /// Get default value with `normal`
- pub fn normal() -> MetricsOverride {
- MetricsOverride::Normal
- }
-
- /// The ToComputedValue implementation, used for @font-face descriptors.
- ///
- /// Valid override percentages must be non-negative; we return -1.0 to indicate
- /// the absence of an override (i.e. 'normal').
- #[inline]
- pub fn compute(&self) -> ComputedPercentage {
- match *self {
- MetricsOverride::Normal => ComputedPercentage(-1.0),
- MetricsOverride::Override(percent) => ComputedPercentage(percent.0.get()),
- }
- }
-}
-
-#[derive(
- Clone,
- Copy,
- Debug,
- MallocSizeOf,
- Parse,
- PartialEq,
- SpecifiedValueInfo,
- ToComputedValue,
- ToCss,
- ToResolvedValue,
- ToShmem,
-)]
-#[repr(u8)]
-/// How to do font-size scaling.
-pub enum XTextScale {
- /// Both min-font-size and text zoom are enabled.
- All,
- /// Text-only zoom is enabled, but min-font-size is not honored.
- ZoomOnly,
- /// Neither of them is enabled.
- None,
-}
-
-impl XTextScale {
- /// Returns whether text zoom is enabled.
- #[inline]
- pub fn text_zoom_enabled(self) -> bool {
- self != Self::None
- }
-}
-
-#[derive(
- Clone,
- Debug,
- MallocSizeOf,
- PartialEq,
- SpecifiedValueInfo,
- ToComputedValue,
- ToCss,
- ToResolvedValue,
- ToShmem,
-)]
-/// Internal property that reflects the lang attribute
-pub struct XLang(#[css(skip)] pub Atom);
-
-impl XLang {
- #[inline]
- /// Get default value for `-x-lang`
- pub fn get_initial_value() -> XLang {
- XLang(atom!(""))
- }
-}
-
-impl Parse for XLang {
- fn parse<'i, 't>(
- _: &ParserContext,
- input: &mut Parser<'i, 't>,
- ) -> Result<XLang, ParseError<'i>> {
- debug_assert!(
- false,
- "Should be set directly by presentation attributes only."
- );
- Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError))
- }
-}
-
-#[cfg_attr(feature = "gecko", derive(MallocSizeOf))]
-#[derive(Clone, Copy, Debug, PartialEq, SpecifiedValueInfo, ToCss, ToShmem)]
-/// Specifies the minimum font size allowed due to changes in scriptlevel.
-/// Ref: https://wiki.mozilla.org/MathML:mstyle
-pub struct MozScriptMinSize(pub NoCalcLength);
-
-impl MozScriptMinSize {
- #[inline]
- /// Calculate initial value of -moz-script-min-size.
- pub fn get_initial_value() -> Length {
- Length::new(DEFAULT_SCRIPT_MIN_SIZE_PT as f32 * PX_PER_PT)
- }
-}
-
-impl Parse for MozScriptMinSize {
- fn parse<'i, 't>(
- _: &ParserContext,
- input: &mut Parser<'i, 't>,
- ) -> Result<MozScriptMinSize, ParseError<'i>> {
- debug_assert!(
- false,
- "Should be set directly by presentation attributes only."
- );
- Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError))
- }
-}
-
-/// A value for the `math-depth` property.
-/// https://mathml-refresh.github.io/mathml-core/#the-math-script-level-property
-#[cfg_attr(feature = "gecko", derive(MallocSizeOf))]
-#[derive(Clone, Copy, Debug, PartialEq, SpecifiedValueInfo, ToCss, ToShmem)]
-pub enum MathDepth {
- /// Increment math-depth if math-style is compact.
- AutoAdd,
-
- /// Add the function's argument to math-depth.
- #[css(function)]
- Add(Integer),
-
- /// Set math-depth to the specified value.
- Absolute(Integer),
-}
-
-impl Parse for MathDepth {
- fn parse<'i, 't>(
- context: &ParserContext,
- input: &mut Parser<'i, 't>,
- ) -> Result<MathDepth, ParseError<'i>> {
- if input
- .try_parse(|i| i.expect_ident_matching("auto-add"))
- .is_ok()
- {
- return Ok(MathDepth::AutoAdd);
- }
- if let Ok(math_depth_value) = input.try_parse(|input| Integer::parse(context, input)) {
- return Ok(MathDepth::Absolute(math_depth_value));
- }
- input.expect_function_matching("add")?;
- let math_depth_delta_value =
- input.parse_nested_block(|input| Integer::parse(context, input))?;
- Ok(MathDepth::Add(math_depth_delta_value))
- }
-}
-
-#[cfg_attr(feature = "gecko", derive(MallocSizeOf))]
-#[derive(
- Clone,
- Copy,
- Debug,
- PartialEq,
- SpecifiedValueInfo,
- ToComputedValue,
- ToCss,
- ToResolvedValue,
- ToShmem,
-)]
-/// Specifies the multiplier to be used to adjust font size
-/// due to changes in scriptlevel.
-///
-/// Ref: https://www.w3.org/TR/MathML3/chapter3.html#presm.mstyle.attrs
-pub struct MozScriptSizeMultiplier(pub f32);
-
-impl MozScriptSizeMultiplier {
- #[inline]
- /// Get default value of `-moz-script-size-multiplier`
- pub fn get_initial_value() -> MozScriptSizeMultiplier {
- MozScriptSizeMultiplier(DEFAULT_SCRIPT_SIZE_MULTIPLIER as f32)
- }
-}
-
-impl Parse for MozScriptSizeMultiplier {
- fn parse<'i, 't>(
- _: &ParserContext,
- input: &mut Parser<'i, 't>,
- ) -> Result<MozScriptSizeMultiplier, ParseError<'i>> {
- debug_assert!(
- false,
- "Should be set directly by presentation attributes only."
- );
- Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError))
- }
-}
-
-impl From<f32> for MozScriptSizeMultiplier {
- fn from(v: f32) -> Self {
- MozScriptSizeMultiplier(v)
- }
-}
-
-impl From<MozScriptSizeMultiplier> for f32 {
- fn from(v: MozScriptSizeMultiplier) -> f32 {
- v.0
- }
-}
diff --git a/components/style/values/specified/gecko.rs b/components/style/values/specified/gecko.rs
deleted file mode 100644
index e721add59cf..00000000000
--- a/components/style/values/specified/gecko.rs
+++ /dev/null
@@ -1,82 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
-
-//! Specified types for legacy Gecko-only properties.
-
-use crate::parser::{Parse, ParserContext};
-use crate::values::computed::{self, Length, LengthPercentage};
-use crate::values::generics::rect::Rect;
-use cssparser::{Parser, Token};
-use std::fmt;
-use style_traits::values::SequenceWriter;
-use style_traits::{CssWriter, ParseError, StyleParseErrorKind, ToCss};
-
-fn parse_pixel_or_percent<'i, 't>(
- _context: &ParserContext,
- input: &mut Parser<'i, 't>,
-) -> Result<LengthPercentage, ParseError<'i>> {
- let location = input.current_source_location();
- let token = input.next()?;
- let value = match *token {
- Token::Dimension {
- value, ref unit, ..
- } => {
- match_ignore_ascii_case! { unit,
- "px" => Ok(LengthPercentage::new_length(Length::new(value))),
- _ => Err(()),
- }
- },
- Token::Percentage { unit_value, .. } => Ok(LengthPercentage::new_percent(
- computed::Percentage(unit_value),
- )),
- _ => Err(()),
- };
- value.map_err(|()| location.new_custom_error(StyleParseErrorKind::UnspecifiedError))
-}
-
-/// The value of an IntersectionObserver's rootMargin property.
-///
-/// Only bare px or percentage values are allowed. Other length units and
-/// calc() values are not allowed.
-///
-/// <https://w3c.github.io/IntersectionObserver/#parse-a-root-margin>
-#[repr(transparent)]
-pub struct IntersectionObserverRootMargin(pub Rect<LengthPercentage>);
-
-impl Parse for IntersectionObserverRootMargin {
- fn parse<'i, 't>(
- context: &ParserContext,
- input: &mut Parser<'i, 't>,
- ) -> Result<Self, ParseError<'i>> {
- use crate::Zero;
- if input.is_exhausted() {
- // If there are zero elements in tokens, set tokens to ["0px"].
- return Ok(IntersectionObserverRootMargin(Rect::all(
- LengthPercentage::zero(),
- )));
- }
- let rect = Rect::parse_with(context, input, parse_pixel_or_percent)?;
- Ok(IntersectionObserverRootMargin(rect))
- }
-}
-
-// Strictly speaking this is not ToCss. It's serializing for DOM. But
-// we can just reuse the infrastructure of this.
-//
-// <https://w3c.github.io/IntersectionObserver/#dom-intersectionobserver-rootmargin>
-impl ToCss for IntersectionObserverRootMargin {
- fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
- where
- W: fmt::Write,
- {
- // We cannot use the ToCss impl of Rect, because that would
- // merge items when they are equal. We want to list them all.
- let mut writer = SequenceWriter::new(dest, " ");
- let rect = &self.0;
- writer.item(&rect.0)?;
- writer.item(&rect.1)?;
- writer.item(&rect.2)?;
- writer.item(&rect.3)
- }
-}
diff --git a/components/style/values/specified/grid.rs b/components/style/values/specified/grid.rs
deleted file mode 100644
index 8bfa4a363bc..00000000000
--- a/components/style/values/specified/grid.rs
+++ /dev/null
@@ -1,349 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
-
-//! CSS handling for the computed value of
-//! [grids](https://drafts.csswg.org/css-grid/)
-
-use crate::parser::{Parse, ParserContext};
-use crate::values::generics::grid::{GridTemplateComponent, ImplicitGridTracks, RepeatCount};
-use crate::values::generics::grid::{LineNameList, TrackBreadth, TrackRepeat, TrackSize};
-use crate::values::generics::grid::{TrackList, TrackListValue};
-use crate::values::specified::{Integer, LengthPercentage};
-use crate::values::{CSSFloat, CustomIdent};
-use cssparser::{ParseError as CssParseError, Parser, Token};
-use std::mem;
-use style_traits::{ParseError, StyleParseErrorKind};
-
-/// Parse a single flexible length.
-pub fn parse_flex<'i, 't>(input: &mut Parser<'i, 't>) -> Result<CSSFloat, ParseError<'i>> {
- let location = input.current_source_location();
- match *input.next()? {
- Token::Dimension {
- value, ref unit, ..
- } if unit.eq_ignore_ascii_case("fr") && value.is_sign_positive() => Ok(value),
- ref t => Err(location.new_unexpected_token_error(t.clone())),
- }
-}
-
-impl<L> TrackBreadth<L> {
- fn parse_keyword<'i, 't>(input: &mut Parser<'i, 't>) -> Result<Self, ParseError<'i>> {
- #[derive(Parse)]
- enum TrackKeyword {
- Auto,
- MaxContent,
- MinContent,
- }
-
- Ok(match TrackKeyword::parse(input)? {
- TrackKeyword::Auto => TrackBreadth::Auto,
- TrackKeyword::MaxContent => TrackBreadth::MaxContent,
- TrackKeyword::MinContent => TrackBreadth::MinContent,
- })
- }
-}
-
-impl Parse for TrackBreadth<LengthPercentage> {
- fn parse<'i, 't>(
- context: &ParserContext,
- input: &mut Parser<'i, 't>,
- ) -> Result<Self, ParseError<'i>> {
- // FIXME: This and other callers in this file should use
- // NonNegativeLengthPercentage instead.
- //
- // Though it seems these cannot be animated so it's ~ok.
- if let Ok(lp) = input.try_parse(|i| LengthPercentage::parse_non_negative(context, i)) {
- return Ok(TrackBreadth::Breadth(lp));
- }
-
- if let Ok(f) = input.try_parse(parse_flex) {
- return Ok(TrackBreadth::Fr(f));
- }
-
- Self::parse_keyword(input)
- }
-}
-
-impl Parse for TrackSize<LengthPercentage> {
- fn parse<'i, 't>(
- context: &ParserContext,
- input: &mut Parser<'i, 't>,
- ) -> Result<Self, ParseError<'i>> {
- if let Ok(b) = input.try_parse(|i| TrackBreadth::parse(context, i)) {
- return Ok(TrackSize::Breadth(b));
- }
-
- if input
- .try_parse(|i| i.expect_function_matching("minmax"))
- .is_ok()
- {
- return input.parse_nested_block(|input| {
- let inflexible_breadth =
- match input.try_parse(|i| LengthPercentage::parse_non_negative(context, i)) {
- Ok(lp) => TrackBreadth::Breadth(lp),
- Err(..) => TrackBreadth::parse_keyword(input)?,
- };
-
- input.expect_comma()?;
- Ok(TrackSize::Minmax(
- inflexible_breadth,
- TrackBreadth::parse(context, input)?,
- ))
- });
- }
-
- input.expect_function_matching("fit-content")?;
- let lp = input.parse_nested_block(|i| LengthPercentage::parse_non_negative(context, i))?;
- Ok(TrackSize::FitContent(TrackBreadth::Breadth(lp)))
- }
-}
-
-impl Parse for ImplicitGridTracks<TrackSize<LengthPercentage>> {
- fn parse<'i, 't>(
- context: &ParserContext,
- input: &mut Parser<'i, 't>,
- ) -> Result<Self, ParseError<'i>> {
- use style_traits::{Separator, Space};
- let track_sizes = Space::parse(input, |i| TrackSize::parse(context, i))?;
- if track_sizes.len() == 1 && track_sizes[0].is_initial() {
- // A single track with the initial value is always represented by an empty slice.
- return Ok(Default::default());
- }
- return Ok(ImplicitGridTracks(track_sizes.into()));
- }
-}
-
-/// Parse the grid line names into a vector of owned strings.
-///
-/// <https://drafts.csswg.org/css-grid/#typedef-line-names>
-pub fn parse_line_names<'i, 't>(
- input: &mut Parser<'i, 't>,
-) -> Result<crate::OwnedSlice<CustomIdent>, ParseError<'i>> {
- input.expect_square_bracket_block()?;
- input.parse_nested_block(|input| {
- let mut values = vec![];
- while let Ok((loc, ident)) = input.try_parse(|i| -> Result<_, CssParseError<()>> {
- Ok((i.current_source_location(), i.expect_ident_cloned()?))
- }) {
- let ident = CustomIdent::from_ident(loc, &ident, &["span", "auto"])?;
- values.push(ident);
- }
-
- Ok(values.into())
- })
-}
-
-/// The type of `repeat` function (only used in parsing).
-///
-/// <https://drafts.csswg.org/css-grid/#typedef-track-repeat>
-#[derive(Clone, Copy, Debug, PartialEq, SpecifiedValueInfo)]
-#[cfg_attr(feature = "servo", derive(MallocSizeOf))]
-enum RepeatType {
- /// [`<auto-repeat>`](https://drafts.csswg.org/css-grid/#typedef-auto-repeat)
- Auto,
- /// [`<track-repeat>`](https://drafts.csswg.org/css-grid/#typedef-track-repeat)
- Normal,
- /// [`<fixed-repeat>`](https://drafts.csswg.org/css-grid/#typedef-fixed-repeat)
- Fixed,
-}
-
-impl TrackRepeat<LengthPercentage, Integer> {
- fn parse_with_repeat_type<'i, 't>(
- context: &ParserContext,
- input: &mut Parser<'i, 't>,
- ) -> Result<(Self, RepeatType), ParseError<'i>> {
- input
- .try_parse(|i| i.expect_function_matching("repeat").map_err(|e| e.into()))
- .and_then(|_| {
- input.parse_nested_block(|input| {
- let count = RepeatCount::parse(context, input)?;
- input.expect_comma()?;
-
- let is_auto = count == RepeatCount::AutoFit || count == RepeatCount::AutoFill;
- let mut repeat_type = if is_auto {
- RepeatType::Auto
- } else {
- // <fixed-size> is a subset of <track-size>, so it should work for both
- RepeatType::Fixed
- };
-
- let mut names = vec![];
- let mut values = vec![];
- let mut current_names;
-
- loop {
- current_names = input.try_parse(parse_line_names).unwrap_or_default();
- if let Ok(track_size) = input.try_parse(|i| TrackSize::parse(context, i)) {
- if !track_size.is_fixed() {
- if is_auto {
- // should be <fixed-size> for <auto-repeat>
- return Err(input
- .new_custom_error(StyleParseErrorKind::UnspecifiedError));
- }
-
- if repeat_type == RepeatType::Fixed {
- repeat_type = RepeatType::Normal // <track-size> for sure
- }
- }
-
- values.push(track_size);
- names.push(current_names);
- } else {
- if values.is_empty() {
- // expecting at least one <track-size>
- return Err(
- input.new_custom_error(StyleParseErrorKind::UnspecifiedError)
- );
- }
-
- names.push(current_names); // final `<line-names>`
- break; // no more <track-size>, breaking
- }
- }
-
- let repeat = TrackRepeat {
- count,
- track_sizes: values.into(),
- line_names: names.into(),
- };
-
- Ok((repeat, repeat_type))
- })
- })
- }
-}
-
-impl Parse for TrackList<LengthPercentage, Integer> {
- fn parse<'i, 't>(
- context: &ParserContext,
- input: &mut Parser<'i, 't>,
- ) -> Result<Self, ParseError<'i>> {
- let mut current_names = vec![];
- let mut names = vec![];
- let mut values = vec![];
-
- // Whether we've parsed an `<auto-repeat>` value.
- let mut auto_repeat_index = None;
- // assume that everything is <fixed-size>. This flag is useful when we encounter <auto-repeat>
- let mut at_least_one_not_fixed = false;
- loop {
- current_names
- .extend_from_slice(&mut input.try_parse(parse_line_names).unwrap_or_default());
- if let Ok(track_size) = input.try_parse(|i| TrackSize::parse(context, i)) {
- if !track_size.is_fixed() {
- at_least_one_not_fixed = true;
- if auto_repeat_index.is_some() {
- // <auto-track-list> only accepts <fixed-size> and <fixed-repeat>
- return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError));
- }
- }
-
- let vec = mem::replace(&mut current_names, vec![]);
- names.push(vec.into());
- values.push(TrackListValue::TrackSize(track_size));
- } else if let Ok((repeat, type_)) =
- input.try_parse(|i| TrackRepeat::parse_with_repeat_type(context, i))
- {
- match type_ {
- RepeatType::Normal => {
- at_least_one_not_fixed = true;
- if auto_repeat_index.is_some() {
- // only <fixed-repeat>
- return Err(
- input.new_custom_error(StyleParseErrorKind::UnspecifiedError)
- );
- }
- },
- RepeatType::Auto => {
- if auto_repeat_index.is_some() || at_least_one_not_fixed {
- // We've either seen <auto-repeat> earlier, or there's at least one non-fixed value
- return Err(
- input.new_custom_error(StyleParseErrorKind::UnspecifiedError)
- );
- }
- auto_repeat_index = Some(values.len());
- },
- RepeatType::Fixed => {},
- }
-
- let vec = mem::replace(&mut current_names, vec![]);
- names.push(vec.into());
- values.push(TrackListValue::TrackRepeat(repeat));
- } else {
- if values.is_empty() && auto_repeat_index.is_none() {
- return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError));
- }
-
- names.push(current_names.into());
- break;
- }
- }
-
- Ok(TrackList {
- auto_repeat_index: auto_repeat_index.unwrap_or(std::usize::MAX),
- values: values.into(),
- line_names: names.into(),
- })
- }
-}
-
-#[cfg(feature = "gecko")]
-#[inline]
-fn allow_grid_template_subgrids() -> bool {
- true
-}
-
-#[cfg(feature = "servo")]
-#[inline]
-fn allow_grid_template_subgrids() -> bool {
- false
-}
-
-#[cfg(feature = "gecko")]
-#[inline]
-fn allow_grid_template_masonry() -> bool {
- static_prefs::pref!("layout.css.grid-template-masonry-value.enabled")
-}
-
-#[cfg(feature = "servo")]
-#[inline]
-fn allow_grid_template_masonry() -> bool {
- false
-}
-
-impl Parse for GridTemplateComponent<LengthPercentage, Integer> {
- fn parse<'i, 't>(
- context: &ParserContext,
- input: &mut Parser<'i, 't>,
- ) -> Result<Self, ParseError<'i>> {
- if input.try_parse(|i| i.expect_ident_matching("none")).is_ok() {
- return Ok(GridTemplateComponent::None);
- }
-
- Self::parse_without_none(context, input)
- }
-}
-
-impl GridTemplateComponent<LengthPercentage, Integer> {
- /// Parses a `GridTemplateComponent<LengthPercentage>` except `none` keyword.
- pub fn parse_without_none<'i, 't>(
- context: &ParserContext,
- input: &mut Parser<'i, 't>,
- ) -> Result<Self, ParseError<'i>> {
- if allow_grid_template_subgrids() {
- if let Ok(t) = input.try_parse(|i| LineNameList::parse(context, i)) {
- return Ok(GridTemplateComponent::Subgrid(Box::new(t)));
- }
- }
- if allow_grid_template_masonry() {
- if input
- .try_parse(|i| i.expect_ident_matching("masonry"))
- .is_ok()
- {
- return Ok(GridTemplateComponent::Masonry);
- }
- }
- let track_list = TrackList::parse(context, input)?;
- Ok(GridTemplateComponent::TrackList(Box::new(track_list)))
- }
-}
diff --git a/components/style/values/specified/image.rs b/components/style/values/specified/image.rs
deleted file mode 100644
index 26f43adf0a5..00000000000
--- a/components/style/values/specified/image.rs
+++ /dev/null
@@ -1,1308 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
-
-//! CSS handling for the specified value of
-//! [`image`][image]s
-//!
-//! [image]: https://drafts.csswg.org/css-images/#image-values
-
-use crate::custom_properties::SpecifiedValue;
-use crate::parser::{Parse, ParserContext};
-use crate::stylesheets::CorsMode;
-use crate::values::generics::image::PaintWorklet;
-use crate::values::generics::image::{
- self as generic, Circle, Ellipse, GradientCompatMode, ShapeExtent,
-};
-use crate::values::generics::position::Position as GenericPosition;
-use crate::values::generics::NonNegative;
-use crate::values::specified::position::{HorizontalPositionKeyword, VerticalPositionKeyword};
-use crate::values::specified::position::{Position, PositionComponent, Side};
-use crate::values::specified::url::SpecifiedImageUrl;
-use crate::values::specified::{
- Angle, AngleOrPercentage, Color, Length, LengthPercentage, NonNegativeLength,
- NonNegativeLengthPercentage, Resolution,
-};
-use crate::values::specified::{Number, NumberOrPercentage, Percentage};
-use crate::Atom;
-use cssparser::{Delimiter, Parser, Token};
-use selectors::parser::SelectorParseErrorKind;
-use std::cmp::Ordering;
-use std::fmt::{self, Write};
-use style_traits::{CssType, CssWriter, KeywordsCollectFn, ParseError};
-use style_traits::{SpecifiedValueInfo, StyleParseErrorKind, ToCss};
-
-/// Specified values for an image according to CSS-IMAGES.
-/// <https://drafts.csswg.org/css-images/#image-values>
-pub type Image =
- generic::Image<Gradient, MozImageRect, SpecifiedImageUrl, Color, Percentage, Resolution>;
-
-// Images should remain small, see https://github.com/servo/servo/pull/18430
-size_of_test!(Image, 40);
-
-/// Specified values for a CSS gradient.
-/// <https://drafts.csswg.org/css-images/#gradients>
-pub type Gradient = generic::Gradient<
- LineDirection,
- LengthPercentage,
- NonNegativeLength,
- NonNegativeLengthPercentage,
- Position,
- Angle,
- AngleOrPercentage,
- Color,
->;
-
-/// Specified values for CSS cross-fade
-/// cross-fade( CrossFadeElement, ...)
-/// <https://drafts.csswg.org/css-images-4/#cross-fade-function>
-pub type CrossFade = generic::CrossFade<Image, Color, Percentage>;
-/// CrossFadeElement = percent? CrossFadeImage
-pub type CrossFadeElement = generic::CrossFadeElement<Image, Color, Percentage>;
-/// CrossFadeImage = image | color
-pub type CrossFadeImage = generic::CrossFadeImage<Image, Color>;
-
-/// `image-set()`
-pub type ImageSet = generic::ImageSet<Image, Resolution>;
-
-/// Each of the arguments to `image-set()`
-pub type ImageSetItem = generic::ImageSetItem<Image, Resolution>;
-
-type LengthPercentageItemList = crate::OwnedSlice<generic::GradientItem<Color, LengthPercentage>>;
-
-#[cfg(feature = "gecko")]
-fn cross_fade_enabled() -> bool {
- static_prefs::pref!("layout.css.cross-fade.enabled")
-}
-
-#[cfg(feature = "servo")]
-fn cross_fade_enabled() -> bool {
- false
-}
-
-#[cfg(feature = "gecko")]
-fn image_set_enabled() -> bool {
- true
-}
-
-#[cfg(feature = "servo")]
-fn image_set_enabled() -> bool {
- false
-}
-
-impl SpecifiedValueInfo for Gradient {
- const SUPPORTED_TYPES: u8 = CssType::GRADIENT;
-
- fn collect_completion_keywords(f: KeywordsCollectFn) {
- // This list here should keep sync with that in Gradient::parse.
- f(&[
- "linear-gradient",
- "-webkit-linear-gradient",
- "-moz-linear-gradient",
- "repeating-linear-gradient",
- "-webkit-repeating-linear-gradient",
- "-moz-repeating-linear-gradient",
- "radial-gradient",
- "-webkit-radial-gradient",
- "-moz-radial-gradient",
- "repeating-radial-gradient",
- "-webkit-repeating-radial-gradient",
- "-moz-repeating-radial-gradient",
- "-webkit-gradient",
- "conic-gradient",
- "repeating-conic-gradient",
- ]);
- }
-}
-
-// Need to manually implement as whether or not cross-fade shows up in
-// completions & etc is dependent on it being enabled.
-impl<Image, Color, Percentage> SpecifiedValueInfo for generic::CrossFade<Image, Color, Percentage> {
- const SUPPORTED_TYPES: u8 = 0;
-
- fn collect_completion_keywords(f: KeywordsCollectFn) {
- if cross_fade_enabled() {
- f(&["cross-fade"]);
- }
- }
-}
-
-impl<Image, Resolution> SpecifiedValueInfo for generic::ImageSet<Image, Resolution> {
- const SUPPORTED_TYPES: u8 = 0;
-
- fn collect_completion_keywords(f: KeywordsCollectFn) {
- if image_set_enabled() {
- f(&["image-set"]);
- }
- }
-}
-
-/// A specified gradient line direction.
-///
-/// FIXME(emilio): This should be generic over Angle.
-#[derive(Clone, Debug, MallocSizeOf, PartialEq, ToShmem)]
-pub enum LineDirection {
- /// An angular direction.
- Angle(Angle),
- /// A horizontal direction.
- Horizontal(HorizontalPositionKeyword),
- /// A vertical direction.
- Vertical(VerticalPositionKeyword),
- /// A direction towards a corner of a box.
- Corner(HorizontalPositionKeyword, VerticalPositionKeyword),
-}
-
-/// A specified ending shape.
-pub type EndingShape = generic::EndingShape<NonNegativeLength, NonNegativeLengthPercentage>;
-
-/// Specified values for `moz-image-rect`
-/// -moz-image-rect(<uri>, top, right, bottom, left);
-#[cfg(all(feature = "gecko", not(feature = "cbindgen")))]
-pub type MozImageRect = generic::GenericMozImageRect<NumberOrPercentage, SpecifiedImageUrl>;
-
-#[cfg(not(feature = "gecko"))]
-#[derive(
- Clone,
- Debug,
- MallocSizeOf,
- PartialEq,
- SpecifiedValueInfo,
- ToComputedValue,
- ToCss,
- ToResolvedValue,
- ToShmem,
-)]
-/// Empty enum on non-Gecko
-pub enum MozImageRect {}
-
-bitflags! {
- struct ParseImageFlags: u8 {
- const FORBID_NONE = 1 << 0;
- const FORBID_IMAGE_SET = 1 << 1;
- const FORBID_NON_URL = 1 << 2;
- }
-}
-
-impl Parse for Image {
- fn parse<'i, 't>(
- context: &ParserContext,
- input: &mut Parser<'i, 't>,
- ) -> Result<Image, ParseError<'i>> {
- Image::parse_with_cors_mode(context, input, CorsMode::None, ParseImageFlags::empty())
- }
-}
-
-impl Image {
- fn parse_with_cors_mode<'i, 't>(
- context: &ParserContext,
- input: &mut Parser<'i, 't>,
- cors_mode: CorsMode,
- flags: ParseImageFlags,
- ) -> Result<Image, ParseError<'i>> {
- if !flags.contains(ParseImageFlags::FORBID_NONE) &&
- input.try_parse(|i| i.expect_ident_matching("none")).is_ok()
- {
- return Ok(generic::Image::None);
- }
-
- if let Ok(url) = input
- .try_parse(|input| SpecifiedImageUrl::parse_with_cors_mode(context, input, cors_mode))
- {
- return Ok(generic::Image::Url(url));
- }
-
- if !flags.contains(ParseImageFlags::FORBID_IMAGE_SET) && image_set_enabled() {
- if let Ok(is) =
- input.try_parse(|input| ImageSet::parse(context, input, cors_mode, flags))
- {
- return Ok(generic::Image::ImageSet(Box::new(is)));
- }
- }
-
- if flags.contains(ParseImageFlags::FORBID_NON_URL) {
- return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError));
- }
-
- if let Ok(gradient) = input.try_parse(|i| Gradient::parse(context, i)) {
- return Ok(generic::Image::Gradient(Box::new(gradient)));
- }
-
- if cross_fade_enabled() {
- if let Ok(cf) =
- input.try_parse(|input| CrossFade::parse(context, input, cors_mode, flags))
- {
- return Ok(generic::Image::CrossFade(Box::new(cf)));
- }
- }
- #[cfg(feature = "servo")]
- {
- if let Ok(paint_worklet) = input.try_parse(|i| PaintWorklet::parse(context, i)) {
- return Ok(generic::Image::PaintWorklet(paint_worklet));
- }
- }
- #[cfg(feature = "gecko")]
- {
- if let Ok(image_rect) =
- input.try_parse(|input| MozImageRect::parse(context, input, cors_mode))
- {
- return Ok(generic::Image::Rect(Box::new(image_rect)));
- }
- Ok(generic::Image::Element(Image::parse_element(input)?))
- }
- #[cfg(not(feature = "gecko"))]
- Err(input.new_error_for_next_token())
- }
-}
-
-impl Image {
- /// Creates an already specified image value from an already resolved URL
- /// for insertion in the cascade.
- #[cfg(feature = "servo")]
- pub fn for_cascade(url: ::servo_arc::Arc<::url::Url>) -> Self {
- use crate::values::CssUrl;
- generic::Image::Url(CssUrl::for_cascade(url))
- }
-
- /// Parses a `-moz-element(# <element-id>)`.
- #[cfg(feature = "gecko")]
- fn parse_element<'i, 't>(input: &mut Parser<'i, 't>) -> Result<Atom, ParseError<'i>> {
- input.try_parse(|i| i.expect_function_matching("-moz-element"))?;
- let location = input.current_source_location();
- input.parse_nested_block(|i| match *i.next()? {
- Token::IDHash(ref id) => Ok(Atom::from(id.as_ref())),
- ref t => Err(location.new_unexpected_token_error(t.clone())),
- })
- }
-
- /// Provides an alternate method for parsing that associates the URL with
- /// anonymous CORS headers.
- pub fn parse_with_cors_anonymous<'i, 't>(
- context: &ParserContext,
- input: &mut Parser<'i, 't>,
- ) -> Result<Image, ParseError<'i>> {
- Self::parse_with_cors_mode(
- context,
- input,
- CorsMode::Anonymous,
- ParseImageFlags::empty(),
- )
- }
-
- /// Provides an alternate method for parsing, but forbidding `none`
- pub fn parse_forbid_none<'i, 't>(
- context: &ParserContext,
- input: &mut Parser<'i, 't>,
- ) -> Result<Image, ParseError<'i>> {
- Self::parse_with_cors_mode(context, input, CorsMode::None, ParseImageFlags::FORBID_NONE)
- }
-
- /// Provides an alternate method for parsing, but only for urls.
- pub fn parse_only_url<'i, 't>(
- context: &ParserContext,
- input: &mut Parser<'i, 't>,
- ) -> Result<Image, ParseError<'i>> {
- Self::parse_with_cors_mode(
- context,
- input,
- CorsMode::None,
- ParseImageFlags::FORBID_NONE | ParseImageFlags::FORBID_NON_URL,
- )
- }
-}
-
-impl CrossFade {
- /// cross-fade() = cross-fade( <cf-image># )
- fn parse<'i, 't>(
- context: &ParserContext,
- input: &mut Parser<'i, 't>,
- cors_mode: CorsMode,
- flags: ParseImageFlags,
- ) -> Result<Self, ParseError<'i>> {
- input.expect_function_matching("cross-fade")?;
- let elements = input.parse_nested_block(|input| {
- input.parse_comma_separated(|input| {
- CrossFadeElement::parse(context, input, cors_mode, flags)
- })
- })?;
- let elements = crate::OwnedSlice::from(elements);
- Ok(Self { elements })
- }
-}
-
-impl CrossFadeElement {
- fn parse_percentage<'i, 't>(
- context: &ParserContext,
- input: &mut Parser<'i, 't>,
- ) -> Option<Percentage> {
- // We clamp our values here as this is the way that Safari and Chrome's
- // implementation handle out-of-bounds percentages but whether or not
- // this behavior follows the specification is still being discussed.
- // See: <https://github.com/w3c/csswg-drafts/issues/5333>
- input
- .try_parse(|input| Percentage::parse_non_negative(context, input))
- .ok()
- .map(|p| p.clamp_to_hundred())
- }
-
- /// <cf-image> = <percentage>? && [ <image> | <color> ]
- fn parse<'i, 't>(
- context: &ParserContext,
- input: &mut Parser<'i, 't>,
- cors_mode: CorsMode,
- flags: ParseImageFlags,
- ) -> Result<Self, ParseError<'i>> {
- // Try and parse a leading percent sign.
- let mut percent = Self::parse_percentage(context, input);
- // Parse the image
- let image = CrossFadeImage::parse(context, input, cors_mode, flags)?;
- // Try and parse a trailing percent sign.
- if percent.is_none() {
- percent = Self::parse_percentage(context, input);
- }
- Ok(Self {
- percent: percent.into(),
- image,
- })
- }
-}
-
-impl CrossFadeImage {
- fn parse<'i, 't>(
- context: &ParserContext,
- input: &mut Parser<'i, 't>,
- cors_mode: CorsMode,
- flags: ParseImageFlags,
- ) -> Result<Self, ParseError<'i>> {
- if let Ok(image) = input.try_parse(|input| {
- Image::parse_with_cors_mode(
- context,
- input,
- cors_mode,
- flags | ParseImageFlags::FORBID_NONE,
- )
- }) {
- return Ok(Self::Image(image));
- }
- Ok(Self::Color(Color::parse(context, input)?))
- }
-}
-
-impl ImageSet {
- fn parse<'i, 't>(
- context: &ParserContext,
- input: &mut Parser<'i, 't>,
- cors_mode: CorsMode,
- flags: ParseImageFlags,
- ) -> Result<Self, ParseError<'i>> {
- let function = input.expect_function()?;
- match_ignore_ascii_case! { &function,
- "-webkit-image-set" | "image-set" => {},
- _ => {
- let func = function.clone();
- return Err(input.new_custom_error(StyleParseErrorKind::UnexpectedFunction(func)));
- }
- }
- let items = input.parse_nested_block(|input| {
- input.parse_comma_separated(|input| {
- ImageSetItem::parse(context, input, cors_mode, flags)
- })
- })?;
- Ok(Self {
- selected_index: std::usize::MAX,
- items: items.into(),
- })
- }
-}
-
-impl ImageSetItem {
- fn parse_type<'i>(p: &mut Parser<'i, '_>) -> Result<crate::OwnedStr, ParseError<'i>> {
- p.expect_function_matching("type")?;
- p.parse_nested_block(|input| Ok(input.expect_string()?.as_ref().to_owned().into()))
- }
-
- fn parse<'i, 't>(
- context: &ParserContext,
- input: &mut Parser<'i, 't>,
- cors_mode: CorsMode,
- flags: ParseImageFlags,
- ) -> Result<Self, ParseError<'i>> {
- let image = match input.try_parse(|i| i.expect_url_or_string()) {
- Ok(url) => Image::Url(SpecifiedImageUrl::parse_from_string(
- url.as_ref().into(),
- context,
- cors_mode,
- )),
- Err(..) => Image::parse_with_cors_mode(
- context,
- input,
- cors_mode,
- flags | ParseImageFlags::FORBID_NONE | ParseImageFlags::FORBID_IMAGE_SET,
- )?,
- };
-
- let mut resolution = input
- .try_parse(|input| Resolution::parse(context, input))
- .ok();
- let mime_type = input.try_parse(Self::parse_type).ok();
-
- // Try to parse resolution after type().
- if mime_type.is_some() && resolution.is_none() {
- resolution = input
- .try_parse(|input| Resolution::parse(context, input))
- .ok();
- }
-
- let resolution = resolution.unwrap_or_else(|| Resolution::from_x(1.0));
- let has_mime_type = mime_type.is_some();
- let mime_type = mime_type.unwrap_or_default();
-
- Ok(Self {
- image,
- resolution,
- has_mime_type,
- mime_type,
- })
- }
-}
-
-impl Parse for Gradient {
- fn parse<'i, 't>(
- context: &ParserContext,
- input: &mut Parser<'i, 't>,
- ) -> Result<Self, ParseError<'i>> {
- enum Shape {
- Linear,
- Radial,
- Conic,
- }
-
- let func = input.expect_function()?;
- let (shape, repeating, compat_mode) = match_ignore_ascii_case! { &func,
- "linear-gradient" => {
- (Shape::Linear, false, GradientCompatMode::Modern)
- },
- "-webkit-linear-gradient" => {
- (Shape::Linear, false, GradientCompatMode::WebKit)
- },
- #[cfg(feature = "gecko")]
- "-moz-linear-gradient" => {
- (Shape::Linear, false, GradientCompatMode::Moz)
- },
- "repeating-linear-gradient" => {
- (Shape::Linear, true, GradientCompatMode::Modern)
- },
- "-webkit-repeating-linear-gradient" => {
- (Shape::Linear, true, GradientCompatMode::WebKit)
- },
- #[cfg(feature = "gecko")]
- "-moz-repeating-linear-gradient" => {
- (Shape::Linear, true, GradientCompatMode::Moz)
- },
- "radial-gradient" => {
- (Shape::Radial, false, GradientCompatMode::Modern)
- },
- "-webkit-radial-gradient" => {
- (Shape::Radial, false, GradientCompatMode::WebKit)
- },
- #[cfg(feature = "gecko")]
- "-moz-radial-gradient" => {
- (Shape::Radial, false, GradientCompatMode::Moz)
- },
- "repeating-radial-gradient" => {
- (Shape::Radial, true, GradientCompatMode::Modern)
- },
- "-webkit-repeating-radial-gradient" => {
- (Shape::Radial, true, GradientCompatMode::WebKit)
- },
- #[cfg(feature = "gecko")]
- "-moz-repeating-radial-gradient" => {
- (Shape::Radial, true, GradientCompatMode::Moz)
- },
- "conic-gradient" => {
- (Shape::Conic, false, GradientCompatMode::Modern)
- },
- "repeating-conic-gradient" => {
- (Shape::Conic, true, GradientCompatMode::Modern)
- },
- "-webkit-gradient" => {
- return input.parse_nested_block(|i| {
- Self::parse_webkit_gradient_argument(context, i)
- });
- },
- _ => {
- let func = func.clone();
- return Err(input.new_custom_error(StyleParseErrorKind::UnexpectedFunction(func)));
- }
- };
-
- Ok(input.parse_nested_block(|i| {
- Ok(match shape {
- Shape::Linear => Self::parse_linear(context, i, repeating, compat_mode)?,
- Shape::Radial => Self::parse_radial(context, i, repeating, compat_mode)?,
- Shape::Conic => Self::parse_conic(context, i, repeating)?,
- })
- })?)
- }
-}
-
-impl Gradient {
- fn parse_webkit_gradient_argument<'i, 't>(
- context: &ParserContext,
- input: &mut Parser<'i, 't>,
- ) -> Result<Self, ParseError<'i>> {
- use crate::values::specified::position::{
- HorizontalPositionKeyword as X, VerticalPositionKeyword as Y,
- };
- type Point = GenericPosition<Component<X>, Component<Y>>;
-
- #[derive(Clone, Copy, Parse)]
- enum Component<S> {
- Center,
- Number(NumberOrPercentage),
- Side(S),
- }
-
- impl LineDirection {
- fn from_points(first: Point, second: Point) -> Self {
- let h_ord = first.horizontal.partial_cmp(&second.horizontal);
- let v_ord = first.vertical.partial_cmp(&second.vertical);
- let (h, v) = match (h_ord, v_ord) {
- (Some(h), Some(v)) => (h, v),
- _ => return LineDirection::Vertical(Y::Bottom),
- };
- match (h, v) {
- (Ordering::Less, Ordering::Less) => LineDirection::Corner(X::Right, Y::Bottom),
- (Ordering::Less, Ordering::Equal) => LineDirection::Horizontal(X::Right),
- (Ordering::Less, Ordering::Greater) => LineDirection::Corner(X::Right, Y::Top),
- (Ordering::Equal, Ordering::Greater) => LineDirection::Vertical(Y::Top),
- (Ordering::Equal, Ordering::Equal) | (Ordering::Equal, Ordering::Less) => {
- LineDirection::Vertical(Y::Bottom)
- },
- (Ordering::Greater, Ordering::Less) => {
- LineDirection::Corner(X::Left, Y::Bottom)
- },
- (Ordering::Greater, Ordering::Equal) => LineDirection::Horizontal(X::Left),
- (Ordering::Greater, Ordering::Greater) => {
- LineDirection::Corner(X::Left, Y::Top)
- },
- }
- }
- }
-
- impl From<Point> for Position {
- fn from(point: Point) -> Self {
- Self::new(point.horizontal.into(), point.vertical.into())
- }
- }
-
- impl Parse for Point {
- fn parse<'i, 't>(
- context: &ParserContext,
- input: &mut Parser<'i, 't>,
- ) -> Result<Self, ParseError<'i>> {
- input.try_parse(|i| {
- let x = Component::parse(context, i)?;
- let y = Component::parse(context, i)?;
-
- Ok(Self::new(x, y))
- })
- }
- }
-
- impl<S: Side> From<Component<S>> for NumberOrPercentage {
- fn from(component: Component<S>) -> Self {
- match component {
- Component::Center => NumberOrPercentage::Percentage(Percentage::new(0.5)),
- Component::Number(number) => number,
- Component::Side(side) => {
- let p = if side.is_start() {
- Percentage::zero()
- } else {
- Percentage::hundred()
- };
- NumberOrPercentage::Percentage(p)
- },
- }
- }
- }
-
- impl<S: Side> From<Component<S>> for PositionComponent<S> {
- fn from(component: Component<S>) -> Self {
- match component {
- Component::Center => PositionComponent::Center,
- Component::Number(NumberOrPercentage::Number(number)) => {
- PositionComponent::Length(Length::from_px(number.value).into())
- },
- Component::Number(NumberOrPercentage::Percentage(p)) => {
- PositionComponent::Length(p.into())
- },
- Component::Side(side) => PositionComponent::Side(side, None),
- }
- }
- }
-
- impl<S: Copy + Side> Component<S> {
- fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
- match (
- NumberOrPercentage::from(*self),
- NumberOrPercentage::from(*other),
- ) {
- (NumberOrPercentage::Percentage(a), NumberOrPercentage::Percentage(b)) => {
- a.get().partial_cmp(&b.get())
- },
- (NumberOrPercentage::Number(a), NumberOrPercentage::Number(b)) => {
- a.value.partial_cmp(&b.value)
- },
- (_, _) => None,
- }
- }
- }
-
- let ident = input.expect_ident_cloned()?;
- input.expect_comma()?;
-
- Ok(match_ignore_ascii_case! { &ident,
- "linear" => {
- let first = Point::parse(context, input)?;
- input.expect_comma()?;
- let second = Point::parse(context, input)?;
-
- let direction = LineDirection::from_points(first, second);
- let items = Gradient::parse_webkit_gradient_stops(context, input, false)?;
-
- generic::Gradient::Linear {
- direction,
- items,
- repeating: false,
- compat_mode: GradientCompatMode::Modern,
- }
- },
- "radial" => {
- let first_point = Point::parse(context, input)?;
- input.expect_comma()?;
- let first_radius = Number::parse_non_negative(context, input)?;
- input.expect_comma()?;
- let second_point = Point::parse(context, input)?;
- input.expect_comma()?;
- let second_radius = Number::parse_non_negative(context, input)?;
-
- let (reverse_stops, point, radius) = if second_radius.value >= first_radius.value {
- (false, second_point, second_radius)
- } else {
- (true, first_point, first_radius)
- };
-
- let rad = Circle::Radius(NonNegative(Length::from_px(radius.value)));
- let shape = generic::EndingShape::Circle(rad);
- let position: Position = point.into();
- let items = Gradient::parse_webkit_gradient_stops(context, input, reverse_stops)?;
-
- generic::Gradient::Radial {
- shape,
- position,
- items,
- repeating: false,
- compat_mode: GradientCompatMode::Modern,
- }
- },
- _ => {
- let e = SelectorParseErrorKind::UnexpectedIdent(ident.clone());
- return Err(input.new_custom_error(e));
- },
- })
- }
-
- fn parse_webkit_gradient_stops<'i, 't>(
- context: &ParserContext,
- input: &mut Parser<'i, 't>,
- reverse_stops: bool,
- ) -> Result<LengthPercentageItemList, ParseError<'i>> {
- let mut items = input
- .try_parse(|i| {
- i.expect_comma()?;
- i.parse_comma_separated(|i| {
- let function = i.expect_function()?.clone();
- let (color, mut p) = i.parse_nested_block(|i| {
- let p = match_ignore_ascii_case! { &function,
- "color-stop" => {
- let p = NumberOrPercentage::parse(context, i)?.to_percentage();
- i.expect_comma()?;
- p
- },
- "from" => Percentage::zero(),
- "to" => Percentage::hundred(),
- _ => {
- return Err(i.new_custom_error(
- StyleParseErrorKind::UnexpectedFunction(function.clone())
- ))
- },
- };
- let color = Color::parse(context, i)?;
- if color == Color::CurrentColor {
- return Err(i.new_custom_error(StyleParseErrorKind::UnspecifiedError));
- }
- Ok((color.into(), p))
- })?;
- if reverse_stops {
- p.reverse();
- }
- Ok(generic::GradientItem::ComplexColorStop {
- color,
- position: p.into(),
- })
- })
- })
- .unwrap_or(vec![]);
-
- if items.is_empty() {
- items = vec![
- generic::GradientItem::ComplexColorStop {
- color: Color::transparent(),
- position: LengthPercentage::zero_percent(),
- },
- generic::GradientItem::ComplexColorStop {
- color: Color::transparent(),
- position: LengthPercentage::hundred_percent(),
- },
- ];
- } else if items.len() == 1 {
- let first = items[0].clone();
- items.push(first);
- } else {
- items.sort_by(|a, b| {
- match (a, b) {
- (
- &generic::GradientItem::ComplexColorStop {
- position: ref a_position,
- ..
- },
- &generic::GradientItem::ComplexColorStop {
- position: ref b_position,
- ..
- },
- ) => match (a_position, b_position) {
- (&LengthPercentage::Percentage(a), &LengthPercentage::Percentage(b)) => {
- return a.0.partial_cmp(&b.0).unwrap_or(Ordering::Equal);
- },
- _ => {},
- },
- _ => {},
- }
- if reverse_stops {
- Ordering::Greater
- } else {
- Ordering::Less
- }
- })
- }
- Ok(items.into())
- }
-
- /// Not used for -webkit-gradient syntax and conic-gradient
- fn parse_stops<'i, 't>(
- context: &ParserContext,
- input: &mut Parser<'i, 't>,
- ) -> Result<LengthPercentageItemList, ParseError<'i>> {
- let items =
- generic::GradientItem::parse_comma_separated(context, input, LengthPercentage::parse)?;
- if items.len() < 2 {
- return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError));
- }
- Ok(items)
- }
-
- /// Parses a linear gradient.
- /// GradientCompatMode can change during `-moz-` prefixed gradient parsing if it come across a `to` keyword.
- fn parse_linear<'i, 't>(
- context: &ParserContext,
- input: &mut Parser<'i, 't>,
- repeating: bool,
- mut compat_mode: GradientCompatMode,
- ) -> Result<Self, ParseError<'i>> {
- let direction = if let Ok(d) =
- input.try_parse(|i| LineDirection::parse(context, i, &mut compat_mode))
- {
- input.expect_comma()?;
- d
- } else {
- match compat_mode {
- GradientCompatMode::Modern => {
- LineDirection::Vertical(VerticalPositionKeyword::Bottom)
- },
- _ => LineDirection::Vertical(VerticalPositionKeyword::Top),
- }
- };
- let items = Gradient::parse_stops(context, input)?;
-
- Ok(Gradient::Linear {
- direction,
- items,
- repeating,
- compat_mode,
- })
- }
-
- /// Parses a radial gradient.
- fn parse_radial<'i, 't>(
- context: &ParserContext,
- input: &mut Parser<'i, 't>,
- repeating: bool,
- compat_mode: GradientCompatMode,
- ) -> Result<Self, ParseError<'i>> {
- let (shape, position) = match compat_mode {
- GradientCompatMode::Modern => {
- let shape = input.try_parse(|i| EndingShape::parse(context, i, compat_mode));
- let position = input.try_parse(|i| {
- i.expect_ident_matching("at")?;
- Position::parse(context, i)
- });
- (shape, position.ok())
- },
- _ => {
- let position = input.try_parse(|i| Position::parse(context, i));
- let shape = input.try_parse(|i| {
- if position.is_ok() {
- i.expect_comma()?;
- }
- EndingShape::parse(context, i, compat_mode)
- });
- (shape, position.ok())
- },
- };
-
- if shape.is_ok() || position.is_some() {
- input.expect_comma()?;
- }
-
- let shape = shape.unwrap_or({
- generic::EndingShape::Ellipse(Ellipse::Extent(ShapeExtent::FarthestCorner))
- });
-
- let position = position.unwrap_or(Position::center());
-
- let items = Gradient::parse_stops(context, input)?;
-
- Ok(Gradient::Radial {
- shape,
- position,
- items,
- repeating,
- compat_mode,
- })
- }
- fn parse_conic<'i, 't>(
- context: &ParserContext,
- input: &mut Parser<'i, 't>,
- repeating: bool,
- ) -> Result<Self, ParseError<'i>> {
- let angle = input.try_parse(|i| {
- i.expect_ident_matching("from")?;
- // Spec allows unitless zero start angles
- // https://drafts.csswg.org/css-images-4/#valdef-conic-gradient-angle
- Angle::parse_with_unitless(context, i)
- });
- let position = input.try_parse(|i| {
- i.expect_ident_matching("at")?;
- Position::parse(context, i)
- });
- if angle.is_ok() || position.is_ok() {
- input.expect_comma()?;
- }
-
- let angle = angle.unwrap_or(Angle::zero());
- let position = position.unwrap_or(Position::center());
- let items = generic::GradientItem::parse_comma_separated(
- context,
- input,
- AngleOrPercentage::parse_with_unitless,
- )?;
-
- if cfg!(feature = "servo") || items.len() < 2 {
- return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError));
- }
-
- Ok(Gradient::Conic {
- angle,
- position,
- items,
- repeating,
- })
- }
-}
-
-impl generic::LineDirection for LineDirection {
- fn points_downwards(&self, compat_mode: GradientCompatMode) -> bool {
- match *self {
- LineDirection::Angle(ref angle) => angle.degrees() == 180.0,
- LineDirection::Vertical(VerticalPositionKeyword::Bottom) => {
- compat_mode == GradientCompatMode::Modern
- },
- LineDirection::Vertical(VerticalPositionKeyword::Top) => {
- compat_mode != GradientCompatMode::Modern
- },
- _ => false,
- }
- }
-
- fn to_css<W>(&self, dest: &mut CssWriter<W>, compat_mode: GradientCompatMode) -> fmt::Result
- where
- W: Write,
- {
- match *self {
- LineDirection::Angle(angle) => angle.to_css(dest),
- LineDirection::Horizontal(x) => {
- if compat_mode == GradientCompatMode::Modern {
- dest.write_str("to ")?;
- }
- x.to_css(dest)
- },
- LineDirection::Vertical(y) => {
- if compat_mode == GradientCompatMode::Modern {
- dest.write_str("to ")?;
- }
- y.to_css(dest)
- },
- LineDirection::Corner(x, y) => {
- if compat_mode == GradientCompatMode::Modern {
- dest.write_str("to ")?;
- }
- x.to_css(dest)?;
- dest.write_char(' ')?;
- y.to_css(dest)
- },
- }
- }
-}
-
-impl LineDirection {
- fn parse<'i, 't>(
- context: &ParserContext,
- input: &mut Parser<'i, 't>,
- compat_mode: &mut GradientCompatMode,
- ) -> Result<Self, ParseError<'i>> {
- // Gradients allow unitless zero angles as an exception, see:
- // https://github.com/w3c/csswg-drafts/issues/1162
- if let Ok(angle) = input.try_parse(|i| Angle::parse_with_unitless(context, i)) {
- return Ok(LineDirection::Angle(angle));
- }
-
- input.try_parse(|i| {
- let to_ident = i.try_parse(|i| i.expect_ident_matching("to"));
- match *compat_mode {
- // `to` keyword is mandatory in modern syntax.
- GradientCompatMode::Modern => to_ident?,
- // Fall back to Modern compatibility mode in case there is a `to` keyword.
- // According to Gecko, `-moz-linear-gradient(to ...)` should serialize like
- // `linear-gradient(to ...)`.
- GradientCompatMode::Moz if to_ident.is_ok() => {
- *compat_mode = GradientCompatMode::Modern
- },
- // There is no `to` keyword in webkit prefixed syntax. If it's consumed,
- // parsing should throw an error.
- GradientCompatMode::WebKit if to_ident.is_ok() => {
- return Err(
- i.new_custom_error(SelectorParseErrorKind::UnexpectedIdent("to".into()))
- );
- },
- _ => {},
- }
-
- if let Ok(x) = i.try_parse(HorizontalPositionKeyword::parse) {
- if let Ok(y) = i.try_parse(VerticalPositionKeyword::parse) {
- return Ok(LineDirection::Corner(x, y));
- }
- return Ok(LineDirection::Horizontal(x));
- }
- let y = VerticalPositionKeyword::parse(i)?;
- if let Ok(x) = i.try_parse(HorizontalPositionKeyword::parse) {
- return Ok(LineDirection::Corner(x, y));
- }
- Ok(LineDirection::Vertical(y))
- })
- }
-}
-
-impl EndingShape {
- fn parse<'i, 't>(
- context: &ParserContext,
- input: &mut Parser<'i, 't>,
- compat_mode: GradientCompatMode,
- ) -> Result<Self, ParseError<'i>> {
- if let Ok(extent) = input.try_parse(|i| ShapeExtent::parse_with_compat_mode(i, compat_mode))
- {
- if input
- .try_parse(|i| i.expect_ident_matching("circle"))
- .is_ok()
- {
- return Ok(generic::EndingShape::Circle(Circle::Extent(extent)));
- }
- let _ = input.try_parse(|i| i.expect_ident_matching("ellipse"));
- return Ok(generic::EndingShape::Ellipse(Ellipse::Extent(extent)));
- }
- if input
- .try_parse(|i| i.expect_ident_matching("circle"))
- .is_ok()
- {
- if let Ok(extent) =
- input.try_parse(|i| ShapeExtent::parse_with_compat_mode(i, compat_mode))
- {
- return Ok(generic::EndingShape::Circle(Circle::Extent(extent)));
- }
- if compat_mode == GradientCompatMode::Modern {
- if let Ok(length) = input.try_parse(|i| NonNegativeLength::parse(context, i)) {
- return Ok(generic::EndingShape::Circle(Circle::Radius(length)));
- }
- }
- return Ok(generic::EndingShape::Circle(Circle::Extent(
- ShapeExtent::FarthestCorner,
- )));
- }
- if input
- .try_parse(|i| i.expect_ident_matching("ellipse"))
- .is_ok()
- {
- if let Ok(extent) =
- input.try_parse(|i| ShapeExtent::parse_with_compat_mode(i, compat_mode))
- {
- return Ok(generic::EndingShape::Ellipse(Ellipse::Extent(extent)));
- }
- if compat_mode == GradientCompatMode::Modern {
- let pair: Result<_, ParseError> = input.try_parse(|i| {
- let x = NonNegativeLengthPercentage::parse(context, i)?;
- let y = NonNegativeLengthPercentage::parse(context, i)?;
- Ok((x, y))
- });
- if let Ok((x, y)) = pair {
- return Ok(generic::EndingShape::Ellipse(Ellipse::Radii(x, y)));
- }
- }
- return Ok(generic::EndingShape::Ellipse(Ellipse::Extent(
- ShapeExtent::FarthestCorner,
- )));
- }
- if let Ok(length) = input.try_parse(|i| NonNegativeLength::parse(context, i)) {
- if let Ok(y) = input.try_parse(|i| NonNegativeLengthPercentage::parse(context, i)) {
- if compat_mode == GradientCompatMode::Modern {
- let _ = input.try_parse(|i| i.expect_ident_matching("ellipse"));
- }
- return Ok(generic::EndingShape::Ellipse(Ellipse::Radii(
- NonNegative(LengthPercentage::from(length.0)),
- y,
- )));
- }
- if compat_mode == GradientCompatMode::Modern {
- let y = input.try_parse(|i| {
- i.expect_ident_matching("ellipse")?;
- NonNegativeLengthPercentage::parse(context, i)
- });
- if let Ok(y) = y {
- return Ok(generic::EndingShape::Ellipse(Ellipse::Radii(
- NonNegative(LengthPercentage::from(length.0)),
- y,
- )));
- }
- let _ = input.try_parse(|i| i.expect_ident_matching("circle"));
- }
-
- return Ok(generic::EndingShape::Circle(Circle::Radius(length)));
- }
- input.try_parse(|i| {
- let x = Percentage::parse_non_negative(context, i)?;
- let y = if let Ok(y) = i.try_parse(|i| NonNegativeLengthPercentage::parse(context, i)) {
- if compat_mode == GradientCompatMode::Modern {
- let _ = i.try_parse(|i| i.expect_ident_matching("ellipse"));
- }
- y
- } else {
- if compat_mode == GradientCompatMode::Modern {
- i.expect_ident_matching("ellipse")?;
- }
- NonNegativeLengthPercentage::parse(context, i)?
- };
- Ok(generic::EndingShape::Ellipse(Ellipse::Radii(
- NonNegative(LengthPercentage::from(x)),
- y,
- )))
- })
- }
-}
-
-impl ShapeExtent {
- fn parse_with_compat_mode<'i, 't>(
- input: &mut Parser<'i, 't>,
- compat_mode: GradientCompatMode,
- ) -> Result<Self, ParseError<'i>> {
- match Self::parse(input)? {
- ShapeExtent::Contain | ShapeExtent::Cover
- if compat_mode == GradientCompatMode::Modern =>
- {
- Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError))
- },
- ShapeExtent::Contain => Ok(ShapeExtent::ClosestSide),
- ShapeExtent::Cover => Ok(ShapeExtent::FarthestCorner),
- keyword => Ok(keyword),
- }
- }
-}
-
-impl<T> generic::GradientItem<Color, T> {
- fn parse_comma_separated<'i, 't>(
- context: &ParserContext,
- input: &mut Parser<'i, 't>,
- parse_position: impl for<'i1, 't1> Fn(&ParserContext, &mut Parser<'i1, 't1>) -> Result<T, ParseError<'i1>>
- + Copy,
- ) -> Result<crate::OwnedSlice<Self>, ParseError<'i>> {
- let mut items = Vec::new();
- let mut seen_stop = false;
-
- loop {
- input.parse_until_before(Delimiter::Comma, |input| {
- if seen_stop {
- if let Ok(hint) = input.try_parse(|i| parse_position(context, i)) {
- seen_stop = false;
- items.push(generic::GradientItem::InterpolationHint(hint));
- return Ok(());
- }
- }
-
- let stop = generic::ColorStop::parse(context, input, parse_position)?;
-
- if let Ok(multi_position) = input.try_parse(|i| parse_position(context, i)) {
- let stop_color = stop.color.clone();
- items.push(stop.into_item());
- items.push(
- generic::ColorStop {
- color: stop_color,
- position: Some(multi_position),
- }
- .into_item(),
- );
- } else {
- items.push(stop.into_item());
- }
-
- seen_stop = true;
- Ok(())
- })?;
-
- match input.next() {
- Err(_) => break,
- Ok(&Token::Comma) => continue,
- Ok(_) => unreachable!(),
- }
- }
-
- if !seen_stop || items.len() < 2 {
- return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError));
- }
- Ok(items.into())
- }
-}
-
-impl<T> generic::ColorStop<Color, T> {
- fn parse<'i, 't>(
- context: &ParserContext,
- input: &mut Parser<'i, 't>,
- parse_position: impl for<'i1, 't1> Fn(
- &ParserContext,
- &mut Parser<'i1, 't1>,
- ) -> Result<T, ParseError<'i1>>,
- ) -> Result<Self, ParseError<'i>> {
- Ok(generic::ColorStop {
- color: Color::parse(context, input)?,
- position: input.try_parse(|i| parse_position(context, i)).ok(),
- })
- }
-}
-
-impl Parse for PaintWorklet {
- fn parse<'i, 't>(
- _context: &ParserContext,
- input: &mut Parser<'i, 't>,
- ) -> Result<Self, ParseError<'i>> {
- input.expect_function_matching("paint")?;
- input.parse_nested_block(|input| {
- let name = Atom::from(&**input.expect_ident()?);
- let arguments = input
- .try_parse(|input| {
- input.expect_comma()?;
- input.parse_comma_separated(|input| SpecifiedValue::parse(input))
- })
- .unwrap_or(vec![]);
- Ok(PaintWorklet { name, arguments })
- })
- }
-}
-
-impl MozImageRect {
- #[cfg(feature = "gecko")]
- fn parse<'i, 't>(
- context: &ParserContext,
- input: &mut Parser<'i, 't>,
- cors_mode: CorsMode,
- ) -> Result<Self, ParseError<'i>> {
- input.try_parse(|i| i.expect_function_matching("-moz-image-rect"))?;
- input.parse_nested_block(|i| {
- let string = i.expect_url_or_string()?;
- let url = SpecifiedImageUrl::parse_from_string(
- string.as_ref().to_owned(),
- context,
- cors_mode,
- );
- i.expect_comma()?;
- let top = NumberOrPercentage::parse_non_negative(context, i)?;
- i.expect_comma()?;
- let right = NumberOrPercentage::parse_non_negative(context, i)?;
- i.expect_comma()?;
- let bottom = NumberOrPercentage::parse_non_negative(context, i)?;
- i.expect_comma()?;
- let left = NumberOrPercentage::parse_non_negative(context, i)?;
- Ok(MozImageRect {
- url,
- top,
- right,
- bottom,
- left,
- })
- })
- }
-}
-
-/// https://drafts.csswg.org/css-images/#propdef-image-rendering
-#[allow(missing_docs)]
-#[derive(
- Clone,
- Copy,
- Debug,
- Eq,
- Hash,
- MallocSizeOf,
- Parse,
- PartialEq,
- SpecifiedValueInfo,
- ToCss,
- ToComputedValue,
- ToResolvedValue,
- ToShmem,
-)]
-#[repr(u8)]
-pub enum ImageRendering {
- Auto,
- #[cfg(feature = "gecko")]
- Smooth,
- #[parse(aliases = "-moz-crisp-edges")]
- CrispEdges,
- Pixelated,
- // From the spec:
- //
- // This property previously accepted the values optimizeSpeed and
- // optimizeQuality. These are now deprecated; a user agent must accept
- // them as valid values but must treat them as having the same behavior
- // as crisp-edges and smooth respectively, and authors must not use
- // them.
- //
- #[cfg(feature = "gecko")]
- Optimizespeed,
- #[cfg(feature = "gecko")]
- Optimizequality,
-}
diff --git a/components/style/values/specified/length.rs b/components/style/values/specified/length.rs
deleted file mode 100644
index 48928a8576d..00000000000
--- a/components/style/values/specified/length.rs
+++ /dev/null
@@ -1,1849 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
-
-//! [Length values][length].
-//!
-//! [length]: https://drafts.csswg.org/css-values/#lengths
-
-use super::{AllowQuirks, Number, Percentage, ToComputedValue};
-use crate::computed_value_flags::ComputedValueFlags;
-use crate::font_metrics::{FontMetrics, FontMetricsOrientation};
-use crate::parser::{Parse, ParserContext};
-use crate::values::computed::{self, CSSPixelLength, Context};
-use crate::values::generics::length as generics;
-use crate::values::generics::length::{
- GenericLengthOrNumber, GenericLengthPercentageOrNormal, GenericMaxSize, GenericSize,
-};
-use crate::values::generics::NonNegative;
-use crate::values::specified::calc::{self, CalcNode};
-use crate::values::specified::NonNegativeNumber;
-use crate::values::CSSFloat;
-use crate::{Zero, ZeroNoPercent};
-use app_units::Au;
-use cssparser::{Parser, Token};
-use std::cmp;
-use std::fmt::{self, Write};
-use style_traits::values::specified::AllowedNumericType;
-use style_traits::{CssWriter, ParseError, SpecifiedValueInfo, StyleParseErrorKind, ToCss};
-
-pub use super::image::Image;
-pub use super::image::{EndingShape as GradientEndingShape, Gradient};
-pub use crate::values::specified::calc::CalcLengthPercentage;
-
-/// Number of pixels per inch
-pub const PX_PER_IN: CSSFloat = 96.;
-/// Number of pixels per centimeter
-pub const PX_PER_CM: CSSFloat = PX_PER_IN / 2.54;
-/// Number of pixels per millimeter
-pub const PX_PER_MM: CSSFloat = PX_PER_IN / 25.4;
-/// Number of pixels per quarter
-pub const PX_PER_Q: CSSFloat = PX_PER_MM / 4.;
-/// Number of pixels per point
-pub const PX_PER_PT: CSSFloat = PX_PER_IN / 72.;
-/// Number of pixels per pica
-pub const PX_PER_PC: CSSFloat = PX_PER_PT * 12.;
-
-/// A font relative length.
-#[derive(Clone, Copy, Debug, MallocSizeOf, PartialEq, ToCss, ToShmem)]
-pub enum FontRelativeLength {
- /// A "em" value: https://drafts.csswg.org/css-values/#em
- #[css(dimension)]
- Em(CSSFloat),
- /// A "ex" value: https://drafts.csswg.org/css-values/#ex
- #[css(dimension)]
- Ex(CSSFloat),
- /// A "ch" value: https://drafts.csswg.org/css-values/#ch
- #[css(dimension)]
- Ch(CSSFloat),
- /// A "cap" value: https://drafts.csswg.org/css-values/#cap
- #[css(dimension)]
- Cap(CSSFloat),
- /// An "ic" value: https://drafts.csswg.org/css-values/#ic
- #[css(dimension)]
- Ic(CSSFloat),
- /// A "rem" value: https://drafts.csswg.org/css-values/#rem
- #[css(dimension)]
- Rem(CSSFloat),
-}
-
-/// A source to resolve font-relative units against
-#[derive(Clone, Copy, Debug, PartialEq)]
-pub enum FontBaseSize {
- /// Use the font-size of the current element.
- CurrentStyle,
- /// Use the inherited font-size.
- InheritedStyle,
-}
-
-impl FontBaseSize {
- /// Calculate the actual size for a given context
- pub fn resolve(&self, context: &Context) -> computed::FontSize {
- match *self {
- Self::CurrentStyle => context.style().get_font().clone_font_size(),
- Self::InheritedStyle => context.style().get_parent_font().clone_font_size(),
- }
- }
-}
-
-impl FontRelativeLength {
- /// Return the unitless, raw value.
- fn unitless_value(&self) -> CSSFloat {
- match *self {
- Self::Em(v) | Self::Ex(v) | Self::Ch(v) | Self::Cap(v) | Self::Ic(v) | Self::Rem(v) => {
- v
- },
- }
- }
-
- // Return the unit, as a string.
- fn unit(&self) -> &'static str {
- match *self {
- Self::Em(_) => "em",
- Self::Ex(_) => "ex",
- Self::Ch(_) => "ch",
- Self::Cap(_) => "cap",
- Self::Ic(_) => "ic",
- Self::Rem(_) => "rem",
- }
- }
-
- fn try_op<O>(&self, other: &Self, op: O) -> Result<Self, ()>
- where
- O: Fn(f32, f32) -> f32,
- {
- use self::FontRelativeLength::*;
-
- if std::mem::discriminant(self) != std::mem::discriminant(other) {
- return Err(());
- }
-
- Ok(match (self, other) {
- (&Em(one), &Em(other)) => Em(op(one, other)),
- (&Ex(one), &Ex(other)) => Ex(op(one, other)),
- (&Ch(one), &Ch(other)) => Ch(op(one, other)),
- (&Cap(one), &Cap(other)) => Cap(op(one, other)),
- (&Ic(one), &Ic(other)) => Ic(op(one, other)),
- (&Rem(one), &Rem(other)) => Rem(op(one, other)),
- // See https://github.com/rust-lang/rust/issues/68867. rustc isn't
- // able to figure it own on its own so we help.
- _ => unsafe {
- match *self {
- Em(..) | Ex(..) | Ch(..) | Cap(..) | Ic(..) | Rem(..) => {},
- }
- debug_unreachable!("Forgot to handle unit in try_op()")
- },
- })
- }
-
- fn map(&self, mut op: impl FnMut(f32) -> f32) -> Self {
- match self {
- Self::Em(x) => Self::Em(op(*x)),
- Self::Ex(x) => Self::Ex(op(*x)),
- Self::Ch(x) => Self::Ch(op(*x)),
- Self::Cap(x) => Self::Cap(op(*x)),
- Self::Ic(x) => Self::Ic(op(*x)),
- Self::Rem(x) => Self::Rem(op(*x)),
- }
- }
-
- /// Computes the font-relative length.
- pub fn to_computed_value(
- &self,
- context: &Context,
- base_size: FontBaseSize,
- ) -> computed::Length {
- let (reference_size, length) = self.reference_font_size_and_length(context, base_size);
- (reference_size * length).finite()
- }
-
- /// Return reference font size.
- ///
- /// We use the base_size flag to pass a different size for computing
- /// font-size and unconstrained font-size.
- ///
- /// This returns a pair, the first one is the reference font size, and the
- /// second one is the unpacked relative length.
- fn reference_font_size_and_length(
- &self,
- context: &Context,
- base_size: FontBaseSize,
- ) -> (computed::Length, CSSFloat) {
- fn query_font_metrics(
- context: &Context,
- base_size: FontBaseSize,
- orientation: FontMetricsOrientation,
- ) -> FontMetrics {
- let retrieve_math_scales = false;
- context.query_font_metrics(base_size, orientation, retrieve_math_scales)
- }
-
- let reference_font_size = base_size.resolve(context);
- match *self {
- Self::Em(length) => {
- if context.for_non_inherited_property && base_size == FontBaseSize::CurrentStyle {
- context
- .rule_cache_conditions
- .borrow_mut()
- .set_font_size_dependency(reference_font_size.computed_size);
- }
-
- (reference_font_size.computed_size(), length)
- },
- Self::Ex(length) => {
- // The x-height is an intrinsically horizontal metric.
- let metrics =
- query_font_metrics(context, base_size, FontMetricsOrientation::Horizontal);
- let reference_size = metrics.x_height.unwrap_or_else(|| {
- // https://drafts.csswg.org/css-values/#ex
- //
- // In the cases where it is impossible or impractical to
- // determine the x-height, a value of 0.5em must be
- // assumed.
- //
- // (But note we use 0.5em of the used, not computed
- // font-size)
- reference_font_size.used_size() * 0.5
- });
- (reference_size, length)
- },
- Self::Ch(length) => {
- // https://drafts.csswg.org/css-values/#ch:
- //
- // Equal to the used advance measure of the “0” (ZERO,
- // U+0030) glyph in the font used to render it. (The advance
- // measure of a glyph is its advance width or height,
- // whichever is in the inline axis of the element.)
- //
- let metrics = query_font_metrics(
- context,
- base_size,
- FontMetricsOrientation::MatchContextPreferHorizontal,
- );
- let reference_size = metrics.zero_advance_measure.unwrap_or_else(|| {
- // https://drafts.csswg.org/css-values/#ch
- //
- // In the cases where it is impossible or impractical to
- // determine the measure of the “0” glyph, it must be
- // assumed to be 0.5em wide by 1em tall. Thus, the ch
- // unit falls back to 0.5em in the general case, and to
- // 1em when it would be typeset upright (i.e.
- // writing-mode is vertical-rl or vertical-lr and
- // text-orientation is upright).
- //
- // Same caveat about computed vs. used font-size applies
- // above.
- let wm = context.style().writing_mode;
- if wm.is_vertical() && wm.is_upright() {
- reference_font_size.used_size()
- } else {
- reference_font_size.used_size() * 0.5
- }
- });
- (reference_size, length)
- },
- Self::Cap(length) => {
- let metrics =
- query_font_metrics(context, base_size, FontMetricsOrientation::Horizontal);
- let reference_size = metrics.cap_height.unwrap_or_else(|| {
- // https://drafts.csswg.org/css-values/#cap
- //
- // In the cases where it is impossible or impractical to
- // determine the cap-height, the font’s ascent must be
- // used.
- //
- metrics.ascent
- });
- (reference_size, length)
- },
- Self::Ic(length) => {
- let metrics = query_font_metrics(
- context,
- base_size,
- FontMetricsOrientation::MatchContextPreferVertical,
- );
- let reference_size = metrics.ic_width.unwrap_or_else(|| {
- // https://drafts.csswg.org/css-values/#ic
- //
- // In the cases where it is impossible or impractical to
- // determine the ideographic advance measure, it must be
- // assumed to be 1em.
- //
- // Same caveat about computed vs. used as for other
- // metric-dependent units.
- reference_font_size.used_size()
- });
- (reference_size, length)
- },
- Self::Rem(length) => {
- // https://drafts.csswg.org/css-values/#rem:
- //
- // When specified on the font-size property of the root
- // element, the rem units refer to the property's initial
- // value.
- //
- let reference_size = if context.builder.is_root_element || context.in_media_query {
- reference_font_size.computed_size()
- } else {
- context.device().root_font_size()
- };
- (reference_size, length)
- },
- }
- }
-}
-
-/// https://drafts.csswg.org/css-values/#viewport-variants
-pub enum ViewportVariant {
- /// https://drafts.csswg.org/css-values/#ua-default-viewport-size
- UADefault,
- /// https://drafts.csswg.org/css-values/#small-viewport-percentage-units
- Small,
- /// https://drafts.csswg.org/css-values/#large-viewport-percentage-units
- Large,
- /// https://drafts.csswg.org/css-values/#dynamic-viewport-percentage-units
- Dynamic,
-}
-
-/// https://drafts.csswg.org/css-values/#viewport-relative-units
-#[derive(PartialEq)]
-enum ViewportUnit {
- /// *vw units.
- Vw,
- /// *vh units.
- Vh,
- /// *vmin units.
- Vmin,
- /// *vmax units.
- Vmax,
- /// *vb units.
- Vb,
- /// *vi units.
- Vi,
-}
-
-/// A viewport-relative length.
-///
-/// <https://drafts.csswg.org/css-values/#viewport-relative-lengths>
-#[derive(Clone, Copy, Debug, MallocSizeOf, PartialEq, ToCss, ToShmem)]
-pub enum ViewportPercentageLength {
- /// <https://drafts.csswg.org/css-values/#valdef-length-vw>
- #[css(dimension)]
- Vw(CSSFloat),
- /// <https://drafts.csswg.org/css-values/#valdef-length-svw>
- #[css(dimension)]
- Svw(CSSFloat),
- /// <https://drafts.csswg.org/css-values/#valdef-length-lvw>
- #[css(dimension)]
- Lvw(CSSFloat),
- /// <https://drafts.csswg.org/css-values/#valdef-length-dvw>
- #[css(dimension)]
- Dvw(CSSFloat),
- /// <https://drafts.csswg.org/css-values/#valdef-length-vh>
- #[css(dimension)]
- Vh(CSSFloat),
- /// <https://drafts.csswg.org/css-values/#valdef-length-svh>
- #[css(dimension)]
- Svh(CSSFloat),
- /// <https://drafts.csswg.org/css-values/#valdef-length-lvh>
- #[css(dimension)]
- Lvh(CSSFloat),
- /// <https://drafts.csswg.org/css-values/#valdef-length-dvh>
- #[css(dimension)]
- Dvh(CSSFloat),
- /// <https://drafts.csswg.org/css-values/#valdef-length-vmin>
- #[css(dimension)]
- Vmin(CSSFloat),
- /// <https://drafts.csswg.org/css-values/#valdef-length-svmin>
- #[css(dimension)]
- Svmin(CSSFloat),
- /// <https://drafts.csswg.org/css-values/#valdef-length-lvmin>
- #[css(dimension)]
- Lvmin(CSSFloat),
- /// <https://drafts.csswg.org/css-values/#valdef-length-dvmin>
- #[css(dimension)]
- Dvmin(CSSFloat),
- /// <https://drafts.csswg.org/css-values/#valdef-length-vmax>
- #[css(dimension)]
- Vmax(CSSFloat),
- /// <https://drafts.csswg.org/css-values/#valdef-length-svmax>
- #[css(dimension)]
- Svmax(CSSFloat),
- /// <https://drafts.csswg.org/css-values/#valdef-length-lvmax>
- #[css(dimension)]
- Lvmax(CSSFloat),
- /// <https://drafts.csswg.org/css-values/#valdef-length-dvmax>
- #[css(dimension)]
- Dvmax(CSSFloat),
- /// <https://drafts.csswg.org/css-values/#valdef-length-vb>
- #[css(dimension)]
- Vb(CSSFloat),
- /// <https://drafts.csswg.org/css-values/#valdef-length-svb>
- #[css(dimension)]
- Svb(CSSFloat),
- /// <https://drafts.csswg.org/css-values/#valdef-length-lvb>
- #[css(dimension)]
- Lvb(CSSFloat),
- /// <https://drafts.csswg.org/css-values/#valdef-length-dvb>
- #[css(dimension)]
- Dvb(CSSFloat),
- /// <https://drafts.csswg.org/css-values/#valdef-length-vi>
- #[css(dimension)]
- Vi(CSSFloat),
- /// <https://drafts.csswg.org/css-values/#valdef-length-svi>
- #[css(dimension)]
- Svi(CSSFloat),
- /// <https://drafts.csswg.org/css-values/#valdef-length-lvi>
- #[css(dimension)]
- Lvi(CSSFloat),
- /// <https://drafts.csswg.org/css-values/#valdef-length-dvi>
- #[css(dimension)]
- Dvi(CSSFloat),
-}
-
-impl ViewportPercentageLength {
- /// Return the unitless, raw value.
- fn unitless_value(&self) -> CSSFloat {
- self.unpack().2
- }
-
- // Return the unit, as a string.
- fn unit(&self) -> &'static str {
- match *self {
- Self::Vw(_) => "vw",
- Self::Lvw(_) => "lvw",
- Self::Svw(_) => "svw",
- Self::Dvw(_) => "dvw",
- Self::Vh(_) => "vh",
- Self::Svh(_) => "svh",
- Self::Lvh(_) => "lvh",
- Self::Dvh(_) => "dvh",
- Self::Vmin(_) => "vmin",
- Self::Svmin(_) => "svmin",
- Self::Lvmin(_) => "lvmin",
- Self::Dvmin(_) => "dvmin",
- Self::Vmax(_) => "vmax",
- Self::Svmax(_) => "svmax",
- Self::Lvmax(_) => "lvmax",
- Self::Dvmax(_) => "dvmax",
- Self::Vb(_) => "vb",
- Self::Svb(_) => "svb",
- Self::Lvb(_) => "lvb",
- Self::Dvb(_) => "dvb",
- Self::Vi(_) => "vi",
- Self::Svi(_) => "svi",
- Self::Lvi(_) => "lvi",
- Self::Dvi(_) => "dvi",
- }
- }
-
- fn unpack(&self) -> (ViewportVariant, ViewportUnit, CSSFloat) {
- match *self {
- Self::Vw(v) => (ViewportVariant::UADefault, ViewportUnit::Vw, v),
- Self::Svw(v) => (ViewportVariant::Small, ViewportUnit::Vw, v),
- Self::Lvw(v) => (ViewportVariant::Large, ViewportUnit::Vw, v),
- Self::Dvw(v) => (ViewportVariant::Dynamic, ViewportUnit::Vw, v),
- Self::Vh(v) => (ViewportVariant::UADefault, ViewportUnit::Vh, v),
- Self::Svh(v) => (ViewportVariant::Small, ViewportUnit::Vh, v),
- Self::Lvh(v) => (ViewportVariant::Large, ViewportUnit::Vh, v),
- Self::Dvh(v) => (ViewportVariant::Dynamic, ViewportUnit::Vh, v),
- Self::Vmin(v) => (ViewportVariant::UADefault, ViewportUnit::Vmin, v),
- Self::Svmin(v) => (ViewportVariant::Small, ViewportUnit::Vmin, v),
- Self::Lvmin(v) => (ViewportVariant::Large, ViewportUnit::Vmin, v),
- Self::Dvmin(v) => (ViewportVariant::Dynamic, ViewportUnit::Vmin, v),
- Self::Vmax(v) => (ViewportVariant::UADefault, ViewportUnit::Vmax, v),
- Self::Svmax(v) => (ViewportVariant::Small, ViewportUnit::Vmax, v),
- Self::Lvmax(v) => (ViewportVariant::Large, ViewportUnit::Vmax, v),
- Self::Dvmax(v) => (ViewportVariant::Dynamic, ViewportUnit::Vmax, v),
- Self::Vb(v) => (ViewportVariant::UADefault, ViewportUnit::Vb, v),
- Self::Svb(v) => (ViewportVariant::Small, ViewportUnit::Vb, v),
- Self::Lvb(v) => (ViewportVariant::Large, ViewportUnit::Vb, v),
- Self::Dvb(v) => (ViewportVariant::Dynamic, ViewportUnit::Vb, v),
- Self::Vi(v) => (ViewportVariant::UADefault, ViewportUnit::Vi, v),
- Self::Svi(v) => (ViewportVariant::Small, ViewportUnit::Vi, v),
- Self::Lvi(v) => (ViewportVariant::Large, ViewportUnit::Vi, v),
- Self::Dvi(v) => (ViewportVariant::Dynamic, ViewportUnit::Vi, v),
- }
- }
-
- fn try_op<O>(&self, other: &Self, op: O) -> Result<Self, ()>
- where
- O: Fn(f32, f32) -> f32,
- {
- use self::ViewportPercentageLength::*;
-
- if std::mem::discriminant(self) != std::mem::discriminant(other) {
- return Err(());
- }
-
- Ok(match (self, other) {
- (&Vw(one), &Vw(other)) => Vw(op(one, other)),
- (&Svw(one), &Svw(other)) => Svw(op(one, other)),
- (&Lvw(one), &Lvw(other)) => Lvw(op(one, other)),
- (&Dvw(one), &Dvw(other)) => Dvw(op(one, other)),
- (&Vh(one), &Vh(other)) => Vh(op(one, other)),
- (&Svh(one), &Svh(other)) => Svh(op(one, other)),
- (&Lvh(one), &Lvh(other)) => Lvh(op(one, other)),
- (&Dvh(one), &Dvh(other)) => Dvh(op(one, other)),
- (&Vmin(one), &Vmin(other)) => Vmin(op(one, other)),
- (&Svmin(one), &Svmin(other)) => Svmin(op(one, other)),
- (&Lvmin(one), &Lvmin(other)) => Lvmin(op(one, other)),
- (&Dvmin(one), &Dvmin(other)) => Dvmin(op(one, other)),
- (&Vmax(one), &Vmax(other)) => Vmax(op(one, other)),
- (&Svmax(one), &Svmax(other)) => Svmax(op(one, other)),
- (&Lvmax(one), &Lvmax(other)) => Lvmax(op(one, other)),
- (&Dvmax(one), &Dvmax(other)) => Dvmax(op(one, other)),
- (&Vb(one), &Vb(other)) => Vb(op(one, other)),
- (&Svb(one), &Svb(other)) => Svb(op(one, other)),
- (&Lvb(one), &Lvb(other)) => Lvb(op(one, other)),
- (&Dvb(one), &Dvb(other)) => Dvb(op(one, other)),
- (&Vi(one), &Vi(other)) => Vi(op(one, other)),
- (&Svi(one), &Svi(other)) => Svi(op(one, other)),
- (&Lvi(one), &Lvi(other)) => Lvi(op(one, other)),
- (&Dvi(one), &Dvi(other)) => Dvi(op(one, other)),
- // See https://github.com/rust-lang/rust/issues/68867. rustc isn't
- // able to figure it own on its own so we help.
- _ => unsafe {
- match *self {
- Vw(..) | Svw(..) | Lvw(..) | Dvw(..) | Vh(..) | Svh(..) | Lvh(..) |
- Dvh(..) | Vmin(..) | Svmin(..) | Lvmin(..) | Dvmin(..) | Vmax(..) |
- Svmax(..) | Lvmax(..) | Dvmax(..) | Vb(..) | Svb(..) | Lvb(..) | Dvb(..) |
- Vi(..) | Svi(..) | Lvi(..) | Dvi(..) => {},
- }
- debug_unreachable!("Forgot to handle unit in try_op()")
- },
- })
- }
-
- fn map(&self, mut op: impl FnMut(f32) -> f32) -> Self {
- match self {
- Self::Vw(x) => Self::Vw(op(*x)),
- Self::Svw(x) => Self::Svw(op(*x)),
- Self::Lvw(x) => Self::Lvw(op(*x)),
- Self::Dvw(x) => Self::Dvw(op(*x)),
- Self::Vh(x) => Self::Vh(op(*x)),
- Self::Svh(x) => Self::Svh(op(*x)),
- Self::Lvh(x) => Self::Lvh(op(*x)),
- Self::Dvh(x) => Self::Dvh(op(*x)),
- Self::Vmin(x) => Self::Vmin(op(*x)),
- Self::Svmin(x) => Self::Svmin(op(*x)),
- Self::Lvmin(x) => Self::Lvmin(op(*x)),
- Self::Dvmin(x) => Self::Dvmin(op(*x)),
- Self::Vmax(x) => Self::Vmax(op(*x)),
- Self::Svmax(x) => Self::Svmax(op(*x)),
- Self::Lvmax(x) => Self::Lvmax(op(*x)),
- Self::Dvmax(x) => Self::Dvmax(op(*x)),
- Self::Vb(x) => Self::Vb(op(*x)),
- Self::Svb(x) => Self::Svb(op(*x)),
- Self::Lvb(x) => Self::Lvb(op(*x)),
- Self::Dvb(x) => Self::Dvb(op(*x)),
- Self::Vi(x) => Self::Vi(op(*x)),
- Self::Svi(x) => Self::Svi(op(*x)),
- Self::Lvi(x) => Self::Lvi(op(*x)),
- Self::Dvi(x) => Self::Dvi(op(*x)),
- }
- }
-
- /// Computes the given viewport-relative length for the given viewport size.
- pub fn to_computed_value(&self, context: &Context) -> CSSPixelLength {
- let (variant, unit, factor) = self.unpack();
- let size = context.viewport_size_for_viewport_unit_resolution(variant);
- let length = match unit {
- ViewportUnit::Vw => size.width,
- ViewportUnit::Vh => size.height,
- ViewportUnit::Vmin => cmp::min(size.width, size.height),
- ViewportUnit::Vmax => cmp::max(size.width, size.height),
- ViewportUnit::Vi | ViewportUnit::Vb => {
- context
- .rule_cache_conditions
- .borrow_mut()
- .set_writing_mode_dependency(context.builder.writing_mode);
- if (unit == ViewportUnit::Vb) == context.style().writing_mode.is_vertical() {
- size.width
- } else {
- size.height
- }
- },
- };
-
- // FIXME: Bug 1396535, we need to fix the extremely small viewport length for transform.
- // See bug 989802. We truncate so that adding multiple viewport units
- // that add up to 100 does not overflow due to rounding differences
- let trunc_scaled = ((length.0 as f64) * factor as f64 / 100.).trunc();
- Au::from_f64_au(if trunc_scaled.is_nan() {
- 0.0f64
- } else {
- trunc_scaled
- })
- .into()
- }
-}
-
-/// HTML5 "character width", as defined in HTML5 § 14.5.4.
-#[derive(Clone, Copy, Debug, MallocSizeOf, PartialEq, ToCss, ToShmem)]
-pub struct CharacterWidth(pub i32);
-
-impl CharacterWidth {
- /// Computes the given character width.
- pub fn to_computed_value(&self, reference_font_size: computed::Length) -> computed::Length {
- // This applies the *converting a character width to pixels* algorithm
- // as specified in HTML5 § 14.5.4.
- //
- // TODO(pcwalton): Find these from the font.
- let average_advance = reference_font_size * 0.5;
- let max_advance = reference_font_size;
- (average_advance * (self.0 as CSSFloat - 1.0) + max_advance).finite()
- }
-}
-
-/// Represents an absolute length with its unit
-#[derive(Clone, Copy, Debug, MallocSizeOf, PartialEq, ToCss, ToShmem)]
-pub enum AbsoluteLength {
- /// An absolute length in pixels (px)
- #[css(dimension)]
- Px(CSSFloat),
- /// An absolute length in inches (in)
- #[css(dimension)]
- In(CSSFloat),
- /// An absolute length in centimeters (cm)
- #[css(dimension)]
- Cm(CSSFloat),
- /// An absolute length in millimeters (mm)
- #[css(dimension)]
- Mm(CSSFloat),
- /// An absolute length in quarter-millimeters (q)
- #[css(dimension)]
- Q(CSSFloat),
- /// An absolute length in points (pt)
- #[css(dimension)]
- Pt(CSSFloat),
- /// An absolute length in pica (pc)
- #[css(dimension)]
- Pc(CSSFloat),
-}
-
-impl AbsoluteLength {
- /// Return the unitless, raw value.
- fn unitless_value(&self) -> CSSFloat {
- match *self {
- Self::Px(v) |
- Self::In(v) |
- Self::Cm(v) |
- Self::Mm(v) |
- Self::Q(v) |
- Self::Pt(v) |
- Self::Pc(v) => v,
- }
- }
-
- // Return the unit, as a string.
- fn unit(&self) -> &'static str {
- match *self {
- Self::Px(_) => "px",
- Self::In(_) => "in",
- Self::Cm(_) => "cm",
- Self::Mm(_) => "mm",
- Self::Q(_) => "q",
- Self::Pt(_) => "pt",
- Self::Pc(_) => "pc",
- }
- }
-
- /// Convert this into a pixel value.
- #[inline]
- pub fn to_px(&self) -> CSSFloat {
- match *self {
- Self::Px(value) => value,
- Self::In(value) => value * PX_PER_IN,
- Self::Cm(value) => value * PX_PER_CM,
- Self::Mm(value) => value * PX_PER_MM,
- Self::Q(value) => value * PX_PER_Q,
- Self::Pt(value) => value * PX_PER_PT,
- Self::Pc(value) => value * PX_PER_PC,
- }
- }
-
- fn try_op<O>(&self, other: &Self, op: O) -> Result<Self, ()>
- where
- O: Fn(f32, f32) -> f32,
- {
- Ok(Self::Px(op(self.to_px(), other.to_px())))
- }
-
- fn map(&self, mut op: impl FnMut(f32) -> f32) -> Self {
- Self::Px(op(self.to_px()))
- }
-}
-
-impl ToComputedValue for AbsoluteLength {
- type ComputedValue = CSSPixelLength;
-
- fn to_computed_value(&self, _: &Context) -> Self::ComputedValue {
- CSSPixelLength::new(self.to_px()).finite()
- }
-
- fn from_computed_value(computed: &Self::ComputedValue) -> Self {
- Self::Px(computed.px())
- }
-}
-
-impl PartialOrd for AbsoluteLength {
- fn partial_cmp(&self, other: &Self) -> Option<cmp::Ordering> {
- self.to_px().partial_cmp(&other.to_px())
- }
-}
-
-/// A container query length.
-///
-/// <https://drafts.csswg.org/css-contain-3/#container-lengths>
-#[derive(Clone, Copy, Debug, MallocSizeOf, PartialEq, ToCss, ToShmem)]
-pub enum ContainerRelativeLength {
- /// 1% of query container's width
- #[css(dimension)]
- Cqw(CSSFloat),
- /// 1% of query container's height
- #[css(dimension)]
- Cqh(CSSFloat),
- /// 1% of query container's inline size
- #[css(dimension)]
- Cqi(CSSFloat),
- /// 1% of query container's block size
- #[css(dimension)]
- Cqb(CSSFloat),
- /// The smaller value of `cqi` or `cqb`
- #[css(dimension)]
- Cqmin(CSSFloat),
- /// The larger value of `cqi` or `cqb`
- #[css(dimension)]
- Cqmax(CSSFloat),
-}
-
-impl ContainerRelativeLength {
- fn unitless_value(&self) -> CSSFloat {
- match *self {
- Self::Cqw(v) |
- Self::Cqh(v) |
- Self::Cqi(v) |
- Self::Cqb(v) |
- Self::Cqmin(v) |
- Self::Cqmax(v) => v,
- }
- }
-
- // Return the unit, as a string.
- fn unit(&self) -> &'static str {
- match *self {
- Self::Cqw(_) => "cqw",
- Self::Cqh(_) => "cqh",
- Self::Cqi(_) => "cqi",
- Self::Cqb(_) => "cqb",
- Self::Cqmin(_) => "cqmin",
- Self::Cqmax(_) => "cqmax",
- }
- }
-
- pub(crate) fn try_op<O>(&self, other: &Self, op: O) -> Result<Self, ()>
- where
- O: Fn(f32, f32) -> f32,
- {
- use self::ContainerRelativeLength::*;
-
- if std::mem::discriminant(self) != std::mem::discriminant(other) {
- return Err(());
- }
-
- Ok(match (self, other) {
- (&Cqw(one), &Cqw(other)) => Cqw(op(one, other)),
- (&Cqh(one), &Cqh(other)) => Cqh(op(one, other)),
- (&Cqi(one), &Cqi(other)) => Cqi(op(one, other)),
- (&Cqb(one), &Cqb(other)) => Cqb(op(one, other)),
- (&Cqmin(one), &Cqmin(other)) => Cqmin(op(one, other)),
- (&Cqmax(one), &Cqmax(other)) => Cqmax(op(one, other)),
-
- // See https://github.com/rust-lang/rust/issues/68867, then
- // https://github.com/rust-lang/rust/pull/95161. rustc isn't
- // able to figure it own on its own so we help.
- _ => unsafe {
- match *self {
- Cqw(..) | Cqh(..) | Cqi(..) | Cqb(..) | Cqmin(..) | Cqmax(..) => {},
- }
- debug_unreachable!("Forgot to handle unit in try_op()")
- },
- })
- }
-
- pub(crate) fn map(&self, mut op: impl FnMut(f32) -> f32) -> Self {
- match self {
- Self::Cqw(x) => Self::Cqw(op(*x)),
- Self::Cqh(x) => Self::Cqh(op(*x)),
- Self::Cqi(x) => Self::Cqi(op(*x)),
- Self::Cqb(x) => Self::Cqb(op(*x)),
- Self::Cqmin(x) => Self::Cqmin(op(*x)),
- Self::Cqmax(x) => Self::Cqmax(op(*x)),
- }
- }
-
- /// Computes the given container-relative length.
- pub fn to_computed_value(&self, context: &Context) -> CSSPixelLength {
- if context.for_non_inherited_property {
- context.rule_cache_conditions.borrow_mut().set_uncacheable();
- }
- context.builder.add_flags(ComputedValueFlags::USES_CONTAINER_UNITS);
-
- let size = context.get_container_size_query();
- let (factor, container_length) = match *self {
- Self::Cqw(v) => (v, size.get_container_width(context)),
- Self::Cqh(v) => (v, size.get_container_height(context)),
- Self::Cqi(v) => (v, size.get_container_inline_size(context)),
- Self::Cqb(v) => (v, size.get_container_block_size(context)),
- Self::Cqmin(v) => (
- v,
- cmp::min(
- size.get_container_inline_size(context),
- size.get_container_block_size(context),
- ),
- ),
- Self::Cqmax(v) => (
- v,
- cmp::max(
- size.get_container_inline_size(context),
- size.get_container_block_size(context),
- ),
- ),
- };
- CSSPixelLength::new((container_length.to_f64_px() * factor as f64 / 100.0) as f32).finite()
- }
-}
-
-#[cfg(feature = "gecko")]
-fn are_container_queries_enabled() -> bool {
- static_prefs::pref!("layout.css.container-queries.enabled")
-}
-#[cfg(feature = "servo")]
-fn are_container_queries_enabled() -> bool {
- false
-}
-
-/// A `<length>` without taking `calc` expressions into account
-///
-/// <https://drafts.csswg.org/css-values/#lengths>
-#[derive(Clone, Copy, Debug, MallocSizeOf, PartialEq, ToShmem)]
-pub enum NoCalcLength {
- /// An absolute length
- ///
- /// <https://drafts.csswg.org/css-values/#absolute-length>
- Absolute(AbsoluteLength),
-
- /// A font-relative length:
- ///
- /// <https://drafts.csswg.org/css-values/#font-relative-lengths>
- FontRelative(FontRelativeLength),
-
- /// A viewport-relative length.
- ///
- /// <https://drafts.csswg.org/css-values/#viewport-relative-lengths>
- ViewportPercentage(ViewportPercentageLength),
-
- /// A container query length.
- ///
- /// <https://drafts.csswg.org/css-contain-3/#container-lengths>
- ContainerRelative(ContainerRelativeLength),
- /// HTML5 "character width", as defined in HTML5 § 14.5.4.
- ///
- /// This cannot be specified by the user directly and is only generated by
- /// `Stylist::synthesize_rules_for_legacy_attributes()`.
- ServoCharacterWidth(CharacterWidth),
-}
-
-impl NoCalcLength {
- /// Return the unitless, raw value.
- pub fn unitless_value(&self) -> CSSFloat {
- match *self {
- Self::Absolute(v) => v.unitless_value(),
- Self::FontRelative(v) => v.unitless_value(),
- Self::ViewportPercentage(v) => v.unitless_value(),
- Self::ContainerRelative(v) => v.unitless_value(),
- Self::ServoCharacterWidth(c) => c.0 as f32,
- }
- }
-
- // Return the unit, as a string.
- fn unit(&self) -> &'static str {
- match *self {
- Self::Absolute(v) => v.unit(),
- Self::FontRelative(v) => v.unit(),
- Self::ViewportPercentage(v) => v.unit(),
- Self::ContainerRelative(v) => v.unit(),
- Self::ServoCharacterWidth(_) => "",
- }
- }
-
- /// Returns whether the value of this length without unit is less than zero.
- pub fn is_negative(&self) -> bool {
- self.unitless_value().is_sign_negative()
- }
-
- /// Returns whether the value of this length without unit is equal to zero.
- pub fn is_zero(&self) -> bool {
- self.unitless_value() == 0.0
- }
-
- /// Returns whether the value of this length without unit is infinite.
- pub fn is_infinite(&self) -> bool {
- self.unitless_value().is_infinite()
- }
-
- /// Returns whether the value of this length without unit is NaN.
- pub fn is_nan(&self) -> bool {
- self.unitless_value().is_nan()
- }
-
- /// Whether text-only zoom should be applied to this length.
- ///
- /// Generally, font-dependent/relative units don't get text-only-zoomed,
- /// because the font they're relative to should be zoomed already.
- pub fn should_zoom_text(&self) -> bool {
- match *self {
- Self::Absolute(..) | Self::ViewportPercentage(..) | Self::ContainerRelative(..) => true,
- Self::ServoCharacterWidth(..) | Self::FontRelative(..) => false,
- }
- }
-
- /// Parse a given absolute or relative dimension.
- pub fn parse_dimension(
- context: &ParserContext,
- value: CSSFloat,
- unit: &str,
- ) -> Result<Self, ()> {
- Ok(match_ignore_ascii_case! { unit,
- "px" => Self::Absolute(AbsoluteLength::Px(value)),
- "in" => Self::Absolute(AbsoluteLength::In(value)),
- "cm" => Self::Absolute(AbsoluteLength::Cm(value)),
- "mm" => Self::Absolute(AbsoluteLength::Mm(value)),
- "q" => Self::Absolute(AbsoluteLength::Q(value)),
- "pt" => Self::Absolute(AbsoluteLength::Pt(value)),
- "pc" => Self::Absolute(AbsoluteLength::Pc(value)),
- // font-relative
- "em" => Self::FontRelative(FontRelativeLength::Em(value)),
- "ex" => Self::FontRelative(FontRelativeLength::Ex(value)),
- "ch" => Self::FontRelative(FontRelativeLength::Ch(value)),
- "cap" => Self::FontRelative(FontRelativeLength::Cap(value)),
- "ic" => Self::FontRelative(FontRelativeLength::Ic(value)),
- "rem" => Self::FontRelative(FontRelativeLength::Rem(value)),
- // viewport percentages
- "vw" if !context.in_page_rule() => {
- Self::ViewportPercentage(ViewportPercentageLength::Vw(value))
- },
- "svw" if !context.in_page_rule() => {
- Self::ViewportPercentage(ViewportPercentageLength::Svw(value))
- },
- "lvw" if !context.in_page_rule() => {
- Self::ViewportPercentage(ViewportPercentageLength::Lvw(value))
- },
- "dvw" if !context.in_page_rule() => {
- Self::ViewportPercentage(ViewportPercentageLength::Dvw(value))
- },
- "vh" if !context.in_page_rule() => {
- Self::ViewportPercentage(ViewportPercentageLength::Vh(value))
- },
- "svh" if !context.in_page_rule() => {
- Self::ViewportPercentage(ViewportPercentageLength::Svh(value))
- },
- "lvh" if !context.in_page_rule() => {
- Self::ViewportPercentage(ViewportPercentageLength::Lvh(value))
- },
- "dvh" if !context.in_page_rule() => {
- Self::ViewportPercentage(ViewportPercentageLength::Dvh(value))
- },
- "vmin" if !context.in_page_rule() => {
- Self::ViewportPercentage(ViewportPercentageLength::Vmin(value))
- },
- "svmin" if !context.in_page_rule() => {
- Self::ViewportPercentage(ViewportPercentageLength::Svmin(value))
- },
- "lvmin" if !context.in_page_rule() => {
- Self::ViewportPercentage(ViewportPercentageLength::Lvmin(value))
- },
- "dvmin" if !context.in_page_rule() => {
- Self::ViewportPercentage(ViewportPercentageLength::Dvmin(value))
- },
- "vmax" if !context.in_page_rule() => {
- Self::ViewportPercentage(ViewportPercentageLength::Vmax(value))
- },
- "svmax" if !context.in_page_rule() => {
- Self::ViewportPercentage(ViewportPercentageLength::Svmax(value))
- },
- "lvmax" if !context.in_page_rule() => {
- Self::ViewportPercentage(ViewportPercentageLength::Lvmax(value))
- },
- "dvmax" if !context.in_page_rule() => {
- Self::ViewportPercentage(ViewportPercentageLength::Dvmax(value))
- },
- "vb" if !context.in_page_rule() => {
- Self::ViewportPercentage(ViewportPercentageLength::Vb(value))
- },
- "svb" if !context.in_page_rule() => {
- Self::ViewportPercentage(ViewportPercentageLength::Svb(value))
- },
- "lvb" if !context.in_page_rule() => {
- Self::ViewportPercentage(ViewportPercentageLength::Lvb(value))
- },
- "dvb" if !context.in_page_rule() => {
- Self::ViewportPercentage(ViewportPercentageLength::Dvb(value))
- },
- "vi" if !context.in_page_rule() => {
- Self::ViewportPercentage(ViewportPercentageLength::Vi(value))
- },
- "svi" if !context.in_page_rule() => {
- Self::ViewportPercentage(ViewportPercentageLength::Svi(value))
- },
- "lvi" if !context.in_page_rule() => {
- Self::ViewportPercentage(ViewportPercentageLength::Lvi(value))
- },
- "dvi" if !context.in_page_rule() => {
- Self::ViewportPercentage(ViewportPercentageLength::Dvi(value))
- },
- // Container query lengths. Inherit the limitation from viewport units since
- // we may fall back to them.
- "cqw" if !context.in_page_rule() && are_container_queries_enabled() => {
- Self::ContainerRelative(ContainerRelativeLength::Cqw(value))
- },
- "cqh" if !context.in_page_rule() && are_container_queries_enabled() => {
- Self::ContainerRelative(ContainerRelativeLength::Cqh(value))
- },
- "cqi" if !context.in_page_rule() && are_container_queries_enabled() => {
- Self::ContainerRelative(ContainerRelativeLength::Cqi(value))
- },
- "cqb" if !context.in_page_rule() && are_container_queries_enabled() => {
- Self::ContainerRelative(ContainerRelativeLength::Cqb(value))
- },
- "cqmin" if !context.in_page_rule() && are_container_queries_enabled() => {
- Self::ContainerRelative(ContainerRelativeLength::Cqmin(value))
- },
- "cqmax" if !context.in_page_rule() && are_container_queries_enabled() => {
- Self::ContainerRelative(ContainerRelativeLength::Cqmax(value))
- },
- _ => return Err(()),
- })
- }
-
- pub(crate) fn try_op<O>(&self, other: &Self, op: O) -> Result<Self, ()>
- where
- O: Fn(f32, f32) -> f32,
- {
- use self::NoCalcLength::*;
-
- if std::mem::discriminant(self) != std::mem::discriminant(other) {
- return Err(());
- }
-
- Ok(match (self, other) {
- (&Absolute(ref one), &Absolute(ref other)) => Absolute(one.try_op(other, op)?),
- (&FontRelative(ref one), &FontRelative(ref other)) => {
- FontRelative(one.try_op(other, op)?)
- },
- (&ViewportPercentage(ref one), &ViewportPercentage(ref other)) => {
- ViewportPercentage(one.try_op(other, op)?)
- },
- (&ContainerRelative(ref one), &ContainerRelative(ref other)) => {
- ContainerRelative(one.try_op(other, op)?)
- },
- (&ServoCharacterWidth(ref one), &ServoCharacterWidth(ref other)) => {
- ServoCharacterWidth(CharacterWidth(op(one.0 as f32, other.0 as f32) as i32))
- },
- // See https://github.com/rust-lang/rust/issues/68867. rustc isn't
- // able to figure it own on its own so we help.
- _ => unsafe {
- match *self {
- Absolute(..) |
- FontRelative(..) |
- ViewportPercentage(..) |
- ContainerRelative(..) |
- ServoCharacterWidth(..) => {},
- }
- debug_unreachable!("Forgot to handle unit in try_op()")
- },
- })
- }
-
- pub(crate) fn map(&self, mut op: impl FnMut(f32) -> f32) -> Self {
- use self::NoCalcLength::*;
-
- match self {
- Absolute(ref one) => Absolute(one.map(op)),
- FontRelative(ref one) => FontRelative(one.map(op)),
- ViewportPercentage(ref one) => ViewportPercentage(one.map(op)),
- ContainerRelative(ref one) => ContainerRelative(one.map(op)),
- ServoCharacterWidth(ref one) => {
- ServoCharacterWidth(CharacterWidth(op(one.0 as f32) as i32))
- },
- }
- }
-
- /// Get a px value without context.
- #[inline]
- pub fn to_computed_pixel_length_without_context(&self) -> Result<CSSFloat, ()> {
- match *self {
- Self::Absolute(len) => Ok(CSSPixelLength::new(len.to_px()).finite().px()),
- _ => Err(()),
- }
- }
-
- /// Get an absolute length from a px value.
- #[inline]
- pub fn from_px(px_value: CSSFloat) -> NoCalcLength {
- NoCalcLength::Absolute(AbsoluteLength::Px(px_value))
- }
-}
-
-impl ToCss for NoCalcLength {
- fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
- where
- W: Write,
- {
- crate::values::serialize_specified_dimension(
- self.unitless_value(),
- self.unit(),
- false,
- dest,
- )
- }
-}
-
-impl SpecifiedValueInfo for NoCalcLength {}
-
-impl PartialOrd for NoCalcLength {
- fn partial_cmp(&self, other: &Self) -> Option<cmp::Ordering> {
- use self::NoCalcLength::*;
-
- if std::mem::discriminant(self) != std::mem::discriminant(other) {
- return None;
- }
-
- match (self, other) {
- (&Absolute(ref one), &Absolute(ref other)) => one.to_px().partial_cmp(&other.to_px()),
- (&FontRelative(ref one), &FontRelative(ref other)) => one.partial_cmp(other),
- (&ViewportPercentage(ref one), &ViewportPercentage(ref other)) => {
- one.partial_cmp(other)
- },
- (&ContainerRelative(ref one), &ContainerRelative(ref other)) => one.partial_cmp(other),
- (&ServoCharacterWidth(ref one), &ServoCharacterWidth(ref other)) => {
- one.0.partial_cmp(&other.0)
- },
- // See https://github.com/rust-lang/rust/issues/68867. rustc isn't
- // able to figure it own on its own so we help.
- _ => unsafe {
- match *self {
- Absolute(..) |
- FontRelative(..) |
- ViewportPercentage(..) |
- ContainerRelative(..) |
- ServoCharacterWidth(..) => {},
- }
- debug_unreachable!("Forgot an arm in partial_cmp?")
- },
- }
- }
-}
-
-impl Zero for NoCalcLength {
- fn zero() -> Self {
- NoCalcLength::Absolute(AbsoluteLength::Px(0.))
- }
-
- fn is_zero(&self) -> bool {
- NoCalcLength::is_zero(self)
- }
-}
-
-/// An extension to `NoCalcLength` to parse `calc` expressions.
-/// This is commonly used for the `<length>` values.
-///
-/// <https://drafts.csswg.org/css-values/#lengths>
-#[derive(Clone, Debug, MallocSizeOf, PartialEq, SpecifiedValueInfo, ToCss, ToShmem)]
-pub enum Length {
- /// The internal length type that cannot parse `calc`
- NoCalc(NoCalcLength),
- /// A calc expression.
- ///
- /// <https://drafts.csswg.org/css-values/#calc-notation>
- Calc(Box<CalcLengthPercentage>),
-}
-
-impl From<NoCalcLength> for Length {
- #[inline]
- fn from(len: NoCalcLength) -> Self {
- Length::NoCalc(len)
- }
-}
-
-impl PartialOrd for FontRelativeLength {
- fn partial_cmp(&self, other: &Self) -> Option<cmp::Ordering> {
- use self::FontRelativeLength::*;
-
- if std::mem::discriminant(self) != std::mem::discriminant(other) {
- return None;
- }
-
- match (self, other) {
- (&Em(ref one), &Em(ref other)) => one.partial_cmp(other),
- (&Ex(ref one), &Ex(ref other)) => one.partial_cmp(other),
- (&Ch(ref one), &Ch(ref other)) => one.partial_cmp(other),
- (&Cap(ref one), &Cap(ref other)) => one.partial_cmp(other),
- (&Ic(ref one), &Ic(ref other)) => one.partial_cmp(other),
- (&Rem(ref one), &Rem(ref other)) => one.partial_cmp(other),
- // See https://github.com/rust-lang/rust/issues/68867. rustc isn't
- // able to figure it own on its own so we help.
- _ => unsafe {
- match *self {
- Em(..) | Ex(..) | Ch(..) | Cap(..) | Ic(..) | Rem(..) => {},
- }
- debug_unreachable!("Forgot an arm in partial_cmp?")
- },
- }
- }
-}
-
-impl PartialOrd for ContainerRelativeLength {
- fn partial_cmp(&self, other: &Self) -> Option<cmp::Ordering> {
- use self::ContainerRelativeLength::*;
-
- if std::mem::discriminant(self) != std::mem::discriminant(other) {
- return None;
- }
-
- match (self, other) {
- (&Cqw(ref one), &Cqw(ref other)) => one.partial_cmp(other),
- (&Cqh(ref one), &Cqh(ref other)) => one.partial_cmp(other),
- (&Cqi(ref one), &Cqi(ref other)) => one.partial_cmp(other),
- (&Cqb(ref one), &Cqb(ref other)) => one.partial_cmp(other),
- (&Cqmin(ref one), &Cqmin(ref other)) => one.partial_cmp(other),
- (&Cqmax(ref one), &Cqmax(ref other)) => one.partial_cmp(other),
-
- // See https://github.com/rust-lang/rust/issues/68867, then
- // https://github.com/rust-lang/rust/pull/95161. rustc isn't
- // able to figure it own on its own so we help.
- _ => unsafe {
- match *self {
- Cqw(..) | Cqh(..) | Cqi(..) | Cqb(..) | Cqmin(..) | Cqmax(..) => {},
- }
- debug_unreachable!("Forgot to handle unit in partial_cmp()")
- },
- }
- }
-}
-
-impl PartialOrd for ViewportPercentageLength {
- fn partial_cmp(&self, other: &Self) -> Option<cmp::Ordering> {
- use self::ViewportPercentageLength::*;
-
- if std::mem::discriminant(self) != std::mem::discriminant(other) {
- return None;
- }
-
- match (self, other) {
- (&Vw(ref one), &Vw(ref other)) => one.partial_cmp(other),
- (&Svw(ref one), &Svw(ref other)) => one.partial_cmp(other),
- (&Lvw(ref one), &Lvw(ref other)) => one.partial_cmp(other),
- (&Dvw(ref one), &Dvw(ref other)) => one.partial_cmp(other),
- (&Vh(ref one), &Vh(ref other)) => one.partial_cmp(other),
- (&Svh(ref one), &Svh(ref other)) => one.partial_cmp(other),
- (&Lvh(ref one), &Lvh(ref other)) => one.partial_cmp(other),
- (&Dvh(ref one), &Dvh(ref other)) => one.partial_cmp(other),
- (&Vmin(ref one), &Vmin(ref other)) => one.partial_cmp(other),
- (&Svmin(ref one), &Svmin(ref other)) => one.partial_cmp(other),
- (&Lvmin(ref one), &Lvmin(ref other)) => one.partial_cmp(other),
- (&Dvmin(ref one), &Dvmin(ref other)) => one.partial_cmp(other),
- (&Vmax(ref one), &Vmax(ref other)) => one.partial_cmp(other),
- (&Svmax(ref one), &Svmax(ref other)) => one.partial_cmp(other),
- (&Lvmax(ref one), &Lvmax(ref other)) => one.partial_cmp(other),
- (&Dvmax(ref one), &Dvmax(ref other)) => one.partial_cmp(other),
- (&Vb(ref one), &Vb(ref other)) => one.partial_cmp(other),
- (&Svb(ref one), &Svb(ref other)) => one.partial_cmp(other),
- (&Lvb(ref one), &Lvb(ref other)) => one.partial_cmp(other),
- (&Dvb(ref one), &Dvb(ref other)) => one.partial_cmp(other),
- (&Vi(ref one), &Vi(ref other)) => one.partial_cmp(other),
- (&Svi(ref one), &Svi(ref other)) => one.partial_cmp(other),
- (&Lvi(ref one), &Lvi(ref other)) => one.partial_cmp(other),
- (&Dvi(ref one), &Dvi(ref other)) => one.partial_cmp(other),
- // See https://github.com/rust-lang/rust/issues/68867. rustc isn't
- // able to figure it own on its own so we help.
- _ => unsafe {
- match *self {
- Vw(..) | Svw(..) | Lvw(..) | Dvw(..) | Vh(..) | Svh(..) | Lvh(..) |
- Dvh(..) | Vmin(..) | Svmin(..) | Lvmin(..) | Dvmin(..) | Vmax(..) |
- Svmax(..) | Lvmax(..) | Dvmax(..) | Vb(..) | Svb(..) | Lvb(..) | Dvb(..) |
- Vi(..) | Svi(..) | Lvi(..) | Dvi(..) => {},
- }
- debug_unreachable!("Forgot an arm in partial_cmp?")
- },
- }
- }
-}
-
-impl Length {
- #[inline]
- fn parse_internal<'i, 't>(
- context: &ParserContext,
- input: &mut Parser<'i, 't>,
- num_context: AllowedNumericType,
- allow_quirks: AllowQuirks,
- ) -> Result<Self, ParseError<'i>> {
- let location = input.current_source_location();
- let token = input.next()?;
- match *token {
- Token::Dimension {
- value, ref unit, ..
- } if num_context.is_ok(context.parsing_mode, value) => {
- NoCalcLength::parse_dimension(context, value, unit)
- .map(Length::NoCalc)
- .map_err(|()| location.new_unexpected_token_error(token.clone()))
- },
- Token::Number { value, .. } if num_context.is_ok(context.parsing_mode, value) => {
- if value != 0. &&
- !context.parsing_mode.allows_unitless_lengths() &&
- !allow_quirks.allowed(context.quirks_mode)
- {
- return Err(location.new_custom_error(StyleParseErrorKind::UnspecifiedError));
- }
- Ok(Length::NoCalc(NoCalcLength::Absolute(AbsoluteLength::Px(
- value,
- ))))
- },
- Token::Function(ref name) => {
- let function = CalcNode::math_function(context, name, location)?;
- let calc = CalcNode::parse_length(context, input, num_context, function)?;
- Ok(Length::Calc(Box::new(calc)))
- },
- ref token => return Err(location.new_unexpected_token_error(token.clone())),
- }
- }
-
- /// Parse a non-negative length
- #[inline]
- pub fn parse_non_negative<'i, 't>(
- context: &ParserContext,
- input: &mut Parser<'i, 't>,
- ) -> Result<Self, ParseError<'i>> {
- Self::parse_non_negative_quirky(context, input, AllowQuirks::No)
- }
-
- /// Parse a non-negative length, allowing quirks.
- #[inline]
- pub fn parse_non_negative_quirky<'i, 't>(
- context: &ParserContext,
- input: &mut Parser<'i, 't>,
- allow_quirks: AllowQuirks,
- ) -> Result<Self, ParseError<'i>> {
- Self::parse_internal(
- context,
- input,
- AllowedNumericType::NonNegative,
- allow_quirks,
- )
- }
-
- /// Get an absolute length from a px value.
- #[inline]
- pub fn from_px(px_value: CSSFloat) -> Length {
- Length::NoCalc(NoCalcLength::from_px(px_value))
- }
-
- /// Get a px value without context.
- pub fn to_computed_pixel_length_without_context(&self) -> Result<CSSFloat, ()> {
- match *self {
- Self::NoCalc(ref l) => l.to_computed_pixel_length_without_context(),
- Self::Calc(ref l) => l.to_computed_pixel_length_without_context(),
- }
- }
-}
-
-impl Parse for Length {
- fn parse<'i, 't>(
- context: &ParserContext,
- input: &mut Parser<'i, 't>,
- ) -> Result<Self, ParseError<'i>> {
- Self::parse_quirky(context, input, AllowQuirks::No)
- }
-}
-
-impl Zero for Length {
- fn zero() -> Self {
- Length::NoCalc(NoCalcLength::zero())
- }
-
- fn is_zero(&self) -> bool {
- // FIXME(emilio): Seems a bit weird to treat calc() unconditionally as
- // non-zero here?
- match *self {
- Length::NoCalc(ref l) => l.is_zero(),
- Length::Calc(..) => false,
- }
- }
-}
-
-impl Length {
- /// Parses a length, with quirks.
- pub fn parse_quirky<'i, 't>(
- context: &ParserContext,
- input: &mut Parser<'i, 't>,
- allow_quirks: AllowQuirks,
- ) -> Result<Self, ParseError<'i>> {
- Self::parse_internal(context, input, AllowedNumericType::All, allow_quirks)
- }
-}
-
-/// A wrapper of Length, whose value must be >= 0.
-pub type NonNegativeLength = NonNegative<Length>;
-
-impl Parse for NonNegativeLength {
- #[inline]
- fn parse<'i, 't>(
- context: &ParserContext,
- input: &mut Parser<'i, 't>,
- ) -> Result<Self, ParseError<'i>> {
- Ok(NonNegative(Length::parse_non_negative(context, input)?))
- }
-}
-
-impl From<NoCalcLength> for NonNegativeLength {
- #[inline]
- fn from(len: NoCalcLength) -> Self {
- NonNegative(Length::NoCalc(len))
- }
-}
-
-impl From<Length> for NonNegativeLength {
- #[inline]
- fn from(len: Length) -> Self {
- NonNegative(len)
- }
-}
-
-impl NonNegativeLength {
- /// Get an absolute length from a px value.
- #[inline]
- pub fn from_px(px_value: CSSFloat) -> Self {
- Length::from_px(px_value.max(0.)).into()
- }
-
- /// Parses a non-negative length, optionally with quirks.
- #[inline]
- pub fn parse_quirky<'i, 't>(
- context: &ParserContext,
- input: &mut Parser<'i, 't>,
- allow_quirks: AllowQuirks,
- ) -> Result<Self, ParseError<'i>> {
- Ok(NonNegative(Length::parse_non_negative_quirky(
- context,
- input,
- allow_quirks,
- )?))
- }
-}
-
-/// A `<length-percentage>` value. This can be either a `<length>`, a
-/// `<percentage>`, or a combination of both via `calc()`.
-///
-/// https://drafts.csswg.org/css-values-4/#typedef-length-percentage
-#[allow(missing_docs)]
-#[derive(Clone, Debug, MallocSizeOf, PartialEq, SpecifiedValueInfo, ToCss, ToShmem)]
-pub enum LengthPercentage {
- Length(NoCalcLength),
- Percentage(computed::Percentage),
- Calc(Box<CalcLengthPercentage>),
-}
-
-impl From<Length> for LengthPercentage {
- fn from(len: Length) -> LengthPercentage {
- match len {
- Length::NoCalc(l) => LengthPercentage::Length(l),
- Length::Calc(l) => LengthPercentage::Calc(l),
- }
- }
-}
-
-impl From<NoCalcLength> for LengthPercentage {
- #[inline]
- fn from(len: NoCalcLength) -> Self {
- LengthPercentage::Length(len)
- }
-}
-
-impl From<Percentage> for LengthPercentage {
- #[inline]
- fn from(pc: Percentage) -> Self {
- if let Some(clamping_mode) = pc.calc_clamping_mode() {
- LengthPercentage::Calc(Box::new(CalcLengthPercentage {
- clamping_mode,
- node: CalcNode::Leaf(calc::Leaf::Percentage(pc.get())),
- }))
- } else {
- LengthPercentage::Percentage(computed::Percentage(pc.get()))
- }
- }
-}
-
-impl From<computed::Percentage> for LengthPercentage {
- #[inline]
- fn from(pc: computed::Percentage) -> Self {
- LengthPercentage::Percentage(pc)
- }
-}
-
-impl Parse for LengthPercentage {
- #[inline]
- fn parse<'i, 't>(
- context: &ParserContext,
- input: &mut Parser<'i, 't>,
- ) -> Result<Self, ParseError<'i>> {
- Self::parse_quirky(context, input, AllowQuirks::No)
- }
-}
-
-impl LengthPercentage {
- #[inline]
- /// Returns a `0%` value.
- pub fn zero_percent() -> LengthPercentage {
- LengthPercentage::Percentage(computed::Percentage::zero())
- }
-
- #[inline]
- /// Returns a `100%` value.
- pub fn hundred_percent() -> LengthPercentage {
- LengthPercentage::Percentage(computed::Percentage::hundred())
- }
-
- fn parse_internal<'i, 't>(
- context: &ParserContext,
- input: &mut Parser<'i, 't>,
- num_context: AllowedNumericType,
- allow_quirks: AllowQuirks,
- ) -> Result<Self, ParseError<'i>> {
- let location = input.current_source_location();
- let token = input.next()?;
- match *token {
- Token::Dimension {
- value, ref unit, ..
- } if num_context.is_ok(context.parsing_mode, value) => {
- return NoCalcLength::parse_dimension(context, value, unit)
- .map(LengthPercentage::Length)
- .map_err(|()| location.new_unexpected_token_error(token.clone()));
- },
- Token::Percentage { unit_value, .. }
- if num_context.is_ok(context.parsing_mode, unit_value) =>
- {
- return Ok(LengthPercentage::Percentage(computed::Percentage(
- unit_value,
- )));
- },
- Token::Number { value, .. } if num_context.is_ok(context.parsing_mode, value) => {
- if value != 0. &&
- !context.parsing_mode.allows_unitless_lengths() &&
- !allow_quirks.allowed(context.quirks_mode)
- {
- return Err(location.new_unexpected_token_error(token.clone()));
- } else {
- return Ok(LengthPercentage::Length(NoCalcLength::from_px(value)));
- }
- },
- Token::Function(ref name) => {
- let function = CalcNode::math_function(context, name, location)?;
- let calc =
- CalcNode::parse_length_or_percentage(context, input, num_context, function)?;
- Ok(LengthPercentage::Calc(Box::new(calc)))
- },
- _ => return Err(location.new_unexpected_token_error(token.clone())),
- }
- }
-
- /// Parses allowing the unitless length quirk.
- /// <https://quirks.spec.whatwg.org/#the-unitless-length-quirk>
- #[inline]
- pub fn parse_quirky<'i, 't>(
- context: &ParserContext,
- input: &mut Parser<'i, 't>,
- allow_quirks: AllowQuirks,
- ) -> Result<Self, ParseError<'i>> {
- Self::parse_internal(context, input, AllowedNumericType::All, allow_quirks)
- }
-
- /// Parse a non-negative length.
- ///
- /// FIXME(emilio): This should be not public and we should use
- /// NonNegativeLengthPercentage instead.
- #[inline]
- pub fn parse_non_negative<'i, 't>(
- context: &ParserContext,
- input: &mut Parser<'i, 't>,
- ) -> Result<Self, ParseError<'i>> {
- Self::parse_non_negative_quirky(context, input, AllowQuirks::No)
- }
-
- /// Parse a non-negative length, with quirks.
- #[inline]
- pub fn parse_non_negative_quirky<'i, 't>(
- context: &ParserContext,
- input: &mut Parser<'i, 't>,
- allow_quirks: AllowQuirks,
- ) -> Result<Self, ParseError<'i>> {
- Self::parse_internal(
- context,
- input,
- AllowedNumericType::NonNegative,
- allow_quirks,
- )
- }
-}
-
-impl Zero for LengthPercentage {
- fn zero() -> Self {
- LengthPercentage::Length(NoCalcLength::zero())
- }
-
- fn is_zero(&self) -> bool {
- match *self {
- LengthPercentage::Length(l) => l.is_zero(),
- LengthPercentage::Percentage(p) => p.0 == 0.0,
- LengthPercentage::Calc(_) => false,
- }
- }
-}
-
-impl ZeroNoPercent for LengthPercentage {
- fn is_zero_no_percent(&self) -> bool {
- match *self {
- LengthPercentage::Percentage(_) => false,
- _ => self.is_zero(),
- }
- }
-}
-
-/// A specified type for `<length-percentage> | auto`.
-pub type LengthPercentageOrAuto = generics::LengthPercentageOrAuto<LengthPercentage>;
-
-impl LengthPercentageOrAuto {
- /// Returns a value representing `0%`.
- #[inline]
- pub fn zero_percent() -> Self {
- generics::LengthPercentageOrAuto::LengthPercentage(LengthPercentage::zero_percent())
- }
-
- /// Parses a length or a percentage, allowing the unitless length quirk.
- /// <https://quirks.spec.whatwg.org/#the-unitless-length-quirk>
- #[inline]
- pub fn parse_quirky<'i, 't>(
- context: &ParserContext,
- input: &mut Parser<'i, 't>,
- allow_quirks: AllowQuirks,
- ) -> Result<Self, ParseError<'i>> {
- Self::parse_with(context, input, |context, input| {
- LengthPercentage::parse_quirky(context, input, allow_quirks)
- })
- }
-}
-
-/// A wrapper of LengthPercentageOrAuto, whose value must be >= 0.
-pub type NonNegativeLengthPercentageOrAuto =
- generics::LengthPercentageOrAuto<NonNegativeLengthPercentage>;
-
-impl NonNegativeLengthPercentageOrAuto {
- /// Returns a value representing `0%`.
- #[inline]
- pub fn zero_percent() -> Self {
- generics::LengthPercentageOrAuto::LengthPercentage(
- NonNegativeLengthPercentage::zero_percent(),
- )
- }
-
- /// Parses a non-negative length-percentage, allowing the unitless length
- /// quirk.
- #[inline]
- pub fn parse_quirky<'i, 't>(
- context: &ParserContext,
- input: &mut Parser<'i, 't>,
- allow_quirks: AllowQuirks,
- ) -> Result<Self, ParseError<'i>> {
- Self::parse_with(context, input, |context, input| {
- NonNegativeLengthPercentage::parse_quirky(context, input, allow_quirks)
- })
- }
-}
-
-/// A wrapper of LengthPercentage, whose value must be >= 0.
-pub type NonNegativeLengthPercentage = NonNegative<LengthPercentage>;
-
-/// Either a NonNegativeLengthPercentage or the `normal` keyword.
-pub type NonNegativeLengthPercentageOrNormal =
- GenericLengthPercentageOrNormal<NonNegativeLengthPercentage>;
-
-impl From<NoCalcLength> for NonNegativeLengthPercentage {
- #[inline]
- fn from(len: NoCalcLength) -> Self {
- NonNegative(LengthPercentage::from(len))
- }
-}
-
-impl Parse for NonNegativeLengthPercentage {
- #[inline]
- fn parse<'i, 't>(
- context: &ParserContext,
- input: &mut Parser<'i, 't>,
- ) -> Result<Self, ParseError<'i>> {
- Self::parse_quirky(context, input, AllowQuirks::No)
- }
-}
-
-impl NonNegativeLengthPercentage {
- #[inline]
- /// Returns a `0%` value.
- pub fn zero_percent() -> Self {
- NonNegative(LengthPercentage::zero_percent())
- }
-
- /// Parses a length or a percentage, allowing the unitless length quirk.
- /// <https://quirks.spec.whatwg.org/#the-unitless-length-quirk>
- #[inline]
- pub fn parse_quirky<'i, 't>(
- context: &ParserContext,
- input: &mut Parser<'i, 't>,
- allow_quirks: AllowQuirks,
- ) -> Result<Self, ParseError<'i>> {
- LengthPercentage::parse_non_negative_quirky(context, input, allow_quirks).map(NonNegative)
- }
-}
-
-/// Either a `<length>` or the `auto` keyword.
-///
-/// Note that we use LengthPercentage just for convenience, since it pretty much
-/// is everything we care about, but we could just add a similar LengthOrAuto
-/// instead if we think getting rid of this weirdness is worth it.
-pub type LengthOrAuto = generics::LengthPercentageOrAuto<Length>;
-
-impl LengthOrAuto {
- /// Parses a length, allowing the unitless length quirk.
- /// <https://quirks.spec.whatwg.org/#the-unitless-length-quirk>
- #[inline]
- pub fn parse_quirky<'i, 't>(
- context: &ParserContext,
- input: &mut Parser<'i, 't>,
- allow_quirks: AllowQuirks,
- ) -> Result<Self, ParseError<'i>> {
- Self::parse_with(context, input, |context, input| {
- Length::parse_quirky(context, input, allow_quirks)
- })
- }
-}
-
-/// Either a non-negative `<length>` or the `auto` keyword.
-pub type NonNegativeLengthOrAuto = generics::LengthPercentageOrAuto<NonNegativeLength>;
-
-/// Either a `<length>` or a `<number>`.
-pub type LengthOrNumber = GenericLengthOrNumber<Length, Number>;
-
-/// A specified value for `min-width`, `min-height`, `width` or `height` property.
-pub type Size = GenericSize<NonNegativeLengthPercentage>;
-
-impl Parse for Size {
- fn parse<'i, 't>(
- context: &ParserContext,
- input: &mut Parser<'i, 't>,
- ) -> Result<Self, ParseError<'i>> {
- Size::parse_quirky(context, input, AllowQuirks::No)
- }
-}
-
-macro_rules! parse_size_non_length {
- ($size:ident, $input:expr, $auto_or_none:expr => $auto_or_none_ident:ident) => {{
- let size = $input.try_parse(|input| {
- Ok(try_match_ident_ignore_ascii_case! { input,
- #[cfg(feature = "gecko")]
- "min-content" | "-moz-min-content" => $size::MinContent,
- #[cfg(feature = "gecko")]
- "max-content" | "-moz-max-content" => $size::MaxContent,
- #[cfg(feature = "gecko")]
- "fit-content" | "-moz-fit-content" => $size::FitContent,
- #[cfg(feature = "gecko")]
- "-moz-available" => $size::MozAvailable,
- $auto_or_none => $size::$auto_or_none_ident,
- })
- });
- if size.is_ok() {
- return size;
- }
- }};
-}
-
-#[cfg(feature = "gecko")]
-fn is_fit_content_function_enabled() -> bool {
- static_prefs::pref!("layout.css.fit-content-function.enabled")
-}
-
-#[cfg(feature = "gecko")]
-macro_rules! parse_fit_content_function {
- ($size:ident, $input:expr, $context:expr, $allow_quirks:expr) => {
- if is_fit_content_function_enabled() {
- if let Ok(length) = $input.try_parse(|input| {
- input.expect_function_matching("fit-content")?;
- input.parse_nested_block(|i| {
- NonNegativeLengthPercentage::parse_quirky($context, i, $allow_quirks)
- })
- }) {
- return Ok($size::FitContentFunction(length));
- }
- }
- };
-}
-
-impl Size {
- /// Parses, with quirks.
- pub fn parse_quirky<'i, 't>(
- context: &ParserContext,
- input: &mut Parser<'i, 't>,
- allow_quirks: AllowQuirks,
- ) -> Result<Self, ParseError<'i>> {
- parse_size_non_length!(Size, input, "auto" => Auto);
- #[cfg(feature = "gecko")]
- parse_fit_content_function!(Size, input, context, allow_quirks);
-
- let length = NonNegativeLengthPercentage::parse_quirky(context, input, allow_quirks)?;
- Ok(GenericSize::LengthPercentage(length))
- }
-
- /// Returns `0%`.
- #[inline]
- pub fn zero_percent() -> Self {
- GenericSize::LengthPercentage(NonNegativeLengthPercentage::zero_percent())
- }
-}
-
-/// A specified value for `max-width` or `max-height` property.
-pub type MaxSize = GenericMaxSize<NonNegativeLengthPercentage>;
-
-impl Parse for MaxSize {
- fn parse<'i, 't>(
- context: &ParserContext,
- input: &mut Parser<'i, 't>,
- ) -> Result<Self, ParseError<'i>> {
- MaxSize::parse_quirky(context, input, AllowQuirks::No)
- }
-}
-
-impl MaxSize {
- /// Parses, with quirks.
- pub fn parse_quirky<'i, 't>(
- context: &ParserContext,
- input: &mut Parser<'i, 't>,
- allow_quirks: AllowQuirks,
- ) -> Result<Self, ParseError<'i>> {
- parse_size_non_length!(MaxSize, input, "none" => None);
- #[cfg(feature = "gecko")]
- parse_fit_content_function!(MaxSize, input, context, allow_quirks);
-
- let length = NonNegativeLengthPercentage::parse_quirky(context, input, allow_quirks)?;
- Ok(GenericMaxSize::LengthPercentage(length))
- }
-}
-
-/// A specified non-negative `<length>` | `<number>`.
-pub type NonNegativeLengthOrNumber = GenericLengthOrNumber<NonNegativeLength, NonNegativeNumber>;
diff --git a/components/style/values/specified/list.rs b/components/style/values/specified/list.rs
deleted file mode 100644
index 693471e4782..00000000000
--- a/components/style/values/specified/list.rs
+++ /dev/null
@@ -1,202 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
-
-//! `list` specified values.
-
-use crate::parser::{Parse, ParserContext};
-#[cfg(feature = "gecko")]
-use crate::values::generics::CounterStyle;
-#[cfg(feature = "gecko")]
-use crate::values::CustomIdent;
-use cssparser::{Parser, Token};
-use style_traits::{ParseError, StyleParseErrorKind};
-
-/// Specified and computed `list-style-type` property.
-#[cfg(feature = "gecko")]
-#[derive(
- Clone,
- Debug,
- Eq,
- MallocSizeOf,
- PartialEq,
- SpecifiedValueInfo,
- ToComputedValue,
- ToCss,
- ToResolvedValue,
- ToShmem,
-)]
-pub enum ListStyleType {
- /// `none`
- None,
- /// <counter-style>
- CounterStyle(CounterStyle),
- /// <string>
- String(String),
-}
-
-#[cfg(feature = "gecko")]
-impl ListStyleType {
- /// Initial specified value for `list-style-type`.
- #[inline]
- pub fn disc() -> Self {
- ListStyleType::CounterStyle(CounterStyle::disc())
- }
-
- /// Convert from gecko keyword to list-style-type.
- ///
- /// This should only be used for mapping type attribute to
- /// list-style-type, and thus only values possible in that
- /// attribute is considered here.
- pub fn from_gecko_keyword(value: u32) -> Self {
- use crate::gecko_bindings::structs;
- let v8 = value as u8;
-
- if v8 == structs::ListStyle_None {
- return ListStyleType::None;
- }
-
- ListStyleType::CounterStyle(CounterStyle::Name(CustomIdent(match v8 {
- structs::ListStyle_Disc => atom!("disc"),
- structs::ListStyle_Circle => atom!("circle"),
- structs::ListStyle_Square => atom!("square"),
- structs::ListStyle_Decimal => atom!("decimal"),
- structs::ListStyle_LowerRoman => atom!("lower-roman"),
- structs::ListStyle_UpperRoman => atom!("upper-roman"),
- structs::ListStyle_LowerAlpha => atom!("lower-alpha"),
- structs::ListStyle_UpperAlpha => atom!("upper-alpha"),
- _ => unreachable!("Unknown counter style keyword value"),
- })))
- }
-
- /// Is this a bullet? (i.e. `list-style-type: disc|circle|square|disclosure-closed|disclosure-open`)
- #[inline]
- pub fn is_bullet(&self) -> bool {
- match self {
- ListStyleType::CounterStyle(ref style) => style.is_bullet(),
- _ => false,
- }
- }
-}
-
-#[cfg(feature = "gecko")]
-impl Parse for ListStyleType {
- fn parse<'i, 't>(
- context: &ParserContext,
- input: &mut Parser<'i, 't>,
- ) -> Result<Self, ParseError<'i>> {
- if let Ok(style) = input.try_parse(|i| CounterStyle::parse(context, i)) {
- return Ok(ListStyleType::CounterStyle(style));
- }
- if input.try_parse(|i| i.expect_ident_matching("none")).is_ok() {
- return Ok(ListStyleType::None);
- }
- Ok(ListStyleType::String(
- input.expect_string()?.as_ref().to_owned(),
- ))
- }
-}
-
-/// A quote pair.
-#[derive(
- Clone,
- Debug,
- MallocSizeOf,
- PartialEq,
- SpecifiedValueInfo,
- ToComputedValue,
- ToCss,
- ToResolvedValue,
- ToShmem,
-)]
-#[repr(C)]
-pub struct QuotePair {
- /// The opening quote.
- pub opening: crate::OwnedStr,
-
- /// The closing quote.
- pub closing: crate::OwnedStr,
-}
-
-/// List of quote pairs for the specified/computed value of `quotes` property.
-#[derive(
- Clone,
- Debug,
- Default,
- MallocSizeOf,
- PartialEq,
- SpecifiedValueInfo,
- ToComputedValue,
- ToCss,
- ToResolvedValue,
- ToShmem,
-)]
-#[repr(transparent)]
-pub struct QuoteList(
- #[css(iterable, if_empty = "none")]
- #[ignore_malloc_size_of = "Arc"]
- pub crate::ArcSlice<QuotePair>,
-);
-
-/// Specified and computed `quotes` property: `auto`, `none`, or a list
-/// of characters.
-#[derive(
- Clone,
- Debug,
- MallocSizeOf,
- PartialEq,
- SpecifiedValueInfo,
- ToComputedValue,
- ToCss,
- ToResolvedValue,
- ToShmem,
-)]
-#[repr(C)]
-pub enum Quotes {
- /// list of quote pairs
- QuoteList(QuoteList),
- /// auto (use lang-dependent quote marks)
- Auto,
-}
-
-impl Parse for Quotes {
- fn parse<'i, 't>(
- _: &ParserContext,
- input: &mut Parser<'i, 't>,
- ) -> Result<Quotes, ParseError<'i>> {
- if input
- .try_parse(|input| input.expect_ident_matching("auto"))
- .is_ok()
- {
- return Ok(Quotes::Auto);
- }
-
- if input
- .try_parse(|input| input.expect_ident_matching("none"))
- .is_ok()
- {
- return Ok(Quotes::QuoteList(QuoteList::default()));
- }
-
- let mut quotes = Vec::new();
- loop {
- let location = input.current_source_location();
- let opening = match input.next() {
- Ok(&Token::QuotedString(ref value)) => value.as_ref().to_owned().into(),
- Ok(t) => return Err(location.new_unexpected_token_error(t.clone())),
- Err(_) => break,
- };
-
- let closing = input.expect_string()?.as_ref().to_owned().into();
- quotes.push(QuotePair { opening, closing });
- }
-
- if !quotes.is_empty() {
- Ok(Quotes::QuoteList(QuoteList(crate::ArcSlice::from_iter(
- quotes.into_iter(),
- ))))
- } else {
- Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError))
- }
- }
-}
diff --git a/components/style/values/specified/mod.rs b/components/style/values/specified/mod.rs
deleted file mode 100644
index da7fcea9b46..00000000000
--- a/components/style/values/specified/mod.rs
+++ /dev/null
@@ -1,953 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
-
-//! Specified values.
-//!
-//! TODO(emilio): Enhance docs.
-
-use super::computed::transform::DirectionVector;
-use super::computed::{Context, ToComputedValue};
-use super::generics::grid::ImplicitGridTracks as GenericImplicitGridTracks;
-use super::generics::grid::{GridLine as GenericGridLine, TrackBreadth as GenericTrackBreadth};
-use super::generics::grid::{TrackList as GenericTrackList, TrackSize as GenericTrackSize};
-use super::generics::transform::IsParallelTo;
-use super::generics::{self, GreaterThanOrEqualToOne, NonNegative};
-use super::{CSSFloat, CSSInteger};
-use crate::context::QuirksMode;
-use crate::parser::{Parse, ParserContext};
-use crate::values::specified::calc::CalcNode;
-use crate::values::{serialize_atom_identifier, serialize_number};
-use crate::{Atom, Namespace, One, Prefix, Zero};
-use cssparser::{Parser, Token};
-use std::fmt::{self, Write};
-use std::ops::Add;
-use style_traits::values::specified::AllowedNumericType;
-use style_traits::{CssWriter, ParseError, SpecifiedValueInfo, StyleParseErrorKind, ToCss};
-
-#[cfg(feature = "gecko")]
-pub use self::align::{AlignContent, AlignItems, AlignSelf, AlignTracks, ContentDistribution};
-#[cfg(feature = "gecko")]
-pub use self::align::{JustifyContent, JustifyItems, JustifySelf, JustifyTracks, SelfAlignment};
-pub use self::angle::{AllowUnitlessZeroAngle, Angle};
-pub use self::animation::{AnimationIterationCount, AnimationName, AnimationTimeline};
-pub use self::animation::{ScrollAxis, ScrollTimelineName, TransitionProperty, ViewTimelineInset};
-pub use self::background::{BackgroundRepeat, BackgroundSize};
-pub use self::basic_shape::FillRule;
-pub use self::border::{
- BorderCornerRadius, BorderImageRepeat, BorderImageSideWidth, BorderImageSlice,
- BorderImageWidth, BorderRadius, BorderSideWidth, BorderSpacing, BorderStyle, LineWidth,
-};
-pub use self::box_::{
- Appearance, BreakBetween, BaselineSource, BreakWithin, Contain, ContainerName, ContainerType,
- Clear, ContainIntrinsicSize, ContentVisibility, Display, Float, LineClamp, Overflow,
- OverflowAnchor, OverflowClipBox, OverscrollBehavior, Perspective, Resize, ScrollbarGutter,
- ScrollSnapAlign, ScrollSnapAxis, ScrollSnapStop, ScrollSnapStrictness, ScrollSnapType,
- TouchAction, VerticalAlign, WillChange,
-};
-pub use self::color::{
- Color, ColorOrAuto, ColorPropertyValue, ColorScheme, ForcedColorAdjust, PrintColorAdjust,
-};
-pub use self::column::ColumnCount;
-pub use self::counters::{Content, ContentItem, CounterIncrement, CounterReset, CounterSet};
-pub use self::easing::TimingFunction;
-pub use self::effects::{BoxShadow, Filter, SimpleShadow};
-pub use self::flex::FlexBasis;
-pub use self::font::{FontFamily, FontLanguageOverride, FontPalette, FontStyle};
-pub use self::font::{FontFeatureSettings, FontVariantLigatures, FontVariantNumeric};
-pub use self::font::{FontSize, FontSizeAdjust, FontSizeKeyword, FontStretch, FontSynthesis};
-pub use self::font::{FontVariantAlternates, FontWeight};
-pub use self::font::{FontVariantEastAsian, FontVariationSettings};
-pub use self::font::{MathDepth, MozScriptMinSize, MozScriptSizeMultiplier, XLang, XTextScale};
-pub use self::image::{EndingShape as GradientEndingShape, Gradient};
-pub use self::image::{Image, ImageRendering, MozImageRect};
-pub use self::length::{AbsoluteLength, CalcLengthPercentage, CharacterWidth};
-pub use self::length::{FontRelativeLength, Length, LengthOrNumber, NonNegativeLengthOrNumber};
-pub use self::length::{LengthOrAuto, LengthPercentage, LengthPercentageOrAuto};
-pub use self::length::{MaxSize, Size};
-pub use self::length::{NoCalcLength, ViewportPercentageLength, ViewportVariant};
-pub use self::length::{
- NonNegativeLength, NonNegativeLengthPercentage, NonNegativeLengthPercentageOrAuto,
-};
-#[cfg(feature = "gecko")]
-pub use self::list::ListStyleType;
-pub use self::list::Quotes;
-pub use self::motion::{OffsetPath, OffsetPosition, OffsetRotate};
-pub use self::outline::OutlineStyle;
-pub use self::page::{PageName, PageOrientation, PageSize, PageSizeOrientation, PaperSize};
-pub use self::percentage::{NonNegativePercentage, Percentage};
-pub use self::position::AspectRatio;
-pub use self::position::{GridAutoFlow, GridTemplateAreas, Position, PositionOrAuto};
-pub use self::position::{MasonryAutoFlow, MasonryItemOrder, MasonryPlacement};
-pub use self::position::{PositionComponent, ZIndex};
-pub use self::ratio::Ratio;
-pub use self::rect::NonNegativeLengthOrNumberRect;
-pub use self::resolution::Resolution;
-pub use self::svg::{DProperty, MozContextProperties};
-pub use self::svg::{SVGLength, SVGOpacity, SVGPaint};
-pub use self::svg::{SVGPaintOrder, SVGStrokeDashArray, SVGWidth};
-pub use self::svg_path::SVGPathData;
-pub use self::text::HyphenateCharacter;
-pub use self::text::RubyPosition;
-pub use self::text::TextAlignLast;
-pub use self::text::TextUnderlinePosition;
-pub use self::text::{InitialLetter, LetterSpacing, LineBreak, LineHeight, TextAlign};
-pub use self::text::{OverflowWrap, TextEmphasisPosition, TextEmphasisStyle, WordBreak};
-pub use self::text::{TextAlignKeyword, TextDecorationLine, TextOverflow, WordSpacing};
-pub use self::text::{TextDecorationLength, TextDecorationSkipInk, TextJustify, TextTransform};
-pub use self::time::Time;
-pub use self::transform::{Rotate, Scale, Transform};
-pub use self::transform::{TransformOrigin, TransformStyle, Translate};
-#[cfg(feature = "gecko")]
-pub use self::ui::CursorImage;
-pub use self::ui::{BoolInteger, Cursor, UserSelect};
-pub use super::generics::grid::GridTemplateComponent as GenericGridTemplateComponent;
-
-#[cfg(feature = "gecko")]
-pub mod align;
-pub mod angle;
-pub mod animation;
-pub mod background;
-pub mod basic_shape;
-pub mod border;
-#[path = "box.rs"]
-pub mod box_;
-pub mod calc;
-pub mod color;
-pub mod column;
-pub mod counters;
-pub mod easing;
-pub mod effects;
-pub mod flex;
-pub mod font;
-#[cfg(feature = "gecko")]
-pub mod gecko;
-pub mod grid;
-pub mod image;
-pub mod length;
-pub mod list;
-pub mod motion;
-pub mod outline;
-pub mod page;
-pub mod percentage;
-pub mod position;
-pub mod ratio;
-pub mod rect;
-pub mod resolution;
-pub mod source_size_list;
-pub mod svg;
-pub mod svg_path;
-pub mod table;
-pub mod text;
-pub mod time;
-pub mod transform;
-pub mod ui;
-pub mod url;
-
-/// <angle> | <percentage>
-/// https://drafts.csswg.org/css-values/#typedef-angle-percentage
-#[allow(missing_docs)]
-#[derive(Clone, Copy, Debug, MallocSizeOf, PartialEq, SpecifiedValueInfo, ToCss, ToShmem)]
-pub enum AngleOrPercentage {
- Percentage(Percentage),
- Angle(Angle),
-}
-
-impl AngleOrPercentage {
- fn parse_internal<'i, 't>(
- context: &ParserContext,
- input: &mut Parser<'i, 't>,
- allow_unitless_zero: AllowUnitlessZeroAngle,
- ) -> Result<Self, ParseError<'i>> {
- if let Ok(per) = input.try_parse(|i| Percentage::parse(context, i)) {
- return Ok(AngleOrPercentage::Percentage(per));
- }
-
- Angle::parse_internal(context, input, allow_unitless_zero).map(AngleOrPercentage::Angle)
- }
-
- /// Allow unitless angles, used for conic-gradients as specified by the spec.
- /// https://drafts.csswg.org/css-images-4/#valdef-conic-gradient-angle
- pub fn parse_with_unitless<'i, 't>(
- context: &ParserContext,
- input: &mut Parser<'i, 't>,
- ) -> Result<Self, ParseError<'i>> {
- AngleOrPercentage::parse_internal(context, input, AllowUnitlessZeroAngle::Yes)
- }
-}
-
-impl Parse for AngleOrPercentage {
- fn parse<'i, 't>(
- context: &ParserContext,
- input: &mut Parser<'i, 't>,
- ) -> Result<Self, ParseError<'i>> {
- AngleOrPercentage::parse_internal(context, input, AllowUnitlessZeroAngle::No)
- }
-}
-
-/// Parse a `<number>` value, with a given clamping mode.
-fn parse_number_with_clamping_mode<'i, 't>(
- context: &ParserContext,
- input: &mut Parser<'i, 't>,
- clamping_mode: AllowedNumericType,
-) -> Result<Number, ParseError<'i>> {
- let location = input.current_source_location();
- match *input.next()? {
- Token::Number { value, .. } if clamping_mode.is_ok(context.parsing_mode, value) => {
- Ok(Number {
- value,
- calc_clamping_mode: None,
- })
- },
- Token::Function(ref name) => {
- let function = CalcNode::math_function(context, name, location)?;
- let value = CalcNode::parse_number(context, input, function)?;
- Ok(Number {
- value,
- calc_clamping_mode: Some(clamping_mode),
- })
- },
- ref t => Err(location.new_unexpected_token_error(t.clone())),
- }
-}
-
-/// A CSS `<number>` specified value.
-///
-/// https://drafts.csswg.org/css-values-3/#number-value
-#[derive(Clone, Copy, Debug, MallocSizeOf, PartialEq, PartialOrd, ToShmem)]
-pub struct Number {
- /// The numeric value itself.
- value: CSSFloat,
- /// If this number came from a calc() expression, this tells how clamping
- /// should be done on the value.
- calc_clamping_mode: Option<AllowedNumericType>,
-}
-
-impl Parse for Number {
- fn parse<'i, 't>(
- context: &ParserContext,
- input: &mut Parser<'i, 't>,
- ) -> Result<Self, ParseError<'i>> {
- parse_number_with_clamping_mode(context, input, AllowedNumericType::All)
- }
-}
-
-impl Number {
- /// Returns a new number with the value `val`.
- #[inline]
- fn new_with_clamping_mode(
- value: CSSFloat,
- calc_clamping_mode: Option<AllowedNumericType>,
- ) -> Self {
- Self {
- value,
- calc_clamping_mode,
- }
- }
-
- /// Returns this percentage as a number.
- pub fn to_percentage(&self) -> Percentage {
- Percentage::new_with_clamping_mode(self.value, self.calc_clamping_mode)
- }
-
- /// Returns a new number with the value `val`.
- #[inline]
- pub fn new(val: CSSFloat) -> Self {
- Self::new_with_clamping_mode(val, None)
- }
-
- /// Returns whether this number came from a `calc()` expression.
- #[inline]
- pub fn was_calc(&self) -> bool {
- self.calc_clamping_mode.is_some()
- }
-
- /// Returns the numeric value, clamped if needed.
- #[inline]
- pub fn get(&self) -> f32 {
- crate::values::normalize(
- self.calc_clamping_mode
- .map_or(self.value, |mode| mode.clamp(self.value)),
- )
- .min(f32::MAX)
- .max(f32::MIN)
- }
-
- #[allow(missing_docs)]
- pub fn parse_non_negative<'i, 't>(
- context: &ParserContext,
- input: &mut Parser<'i, 't>,
- ) -> Result<Number, ParseError<'i>> {
- parse_number_with_clamping_mode(context, input, AllowedNumericType::NonNegative)
- }
-
- #[allow(missing_docs)]
- pub fn parse_at_least_one<'i, 't>(
- context: &ParserContext,
- input: &mut Parser<'i, 't>,
- ) -> Result<Number, ParseError<'i>> {
- parse_number_with_clamping_mode(context, input, AllowedNumericType::AtLeastOne)
- }
-
- /// Clamp to 1.0 if the value is over 1.0.
- #[inline]
- pub fn clamp_to_one(self) -> Self {
- Number {
- value: self.value.min(1.),
- calc_clamping_mode: self.calc_clamping_mode,
- }
- }
-}
-
-impl ToComputedValue for Number {
- type ComputedValue = CSSFloat;
-
- #[inline]
- fn to_computed_value(&self, _: &Context) -> CSSFloat {
- self.get()
- }
-
- #[inline]
- fn from_computed_value(computed: &CSSFloat) -> Self {
- Number {
- value: *computed,
- calc_clamping_mode: None,
- }
- }
-}
-
-impl ToCss for Number {
- fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
- where
- W: Write,
- {
- serialize_number(self.value, self.calc_clamping_mode.is_some(), dest)
- }
-}
-
-impl IsParallelTo for (Number, Number, Number) {
- fn is_parallel_to(&self, vector: &DirectionVector) -> bool {
- use euclid::approxeq::ApproxEq;
- // If a and b is parallel, the angle between them is 0deg, so
- // a x b = |a|*|b|*sin(0)*n = 0 * n, |a x b| == 0.
- let self_vector = DirectionVector::new(self.0.get(), self.1.get(), self.2.get());
- self_vector
- .cross(*vector)
- .square_length()
- .approx_eq(&0.0f32)
- }
-}
-
-impl SpecifiedValueInfo for Number {}
-
-impl Add for Number {
- type Output = Self;
-
- fn add(self, other: Self) -> Self {
- Self::new(self.get() + other.get())
- }
-}
-
-impl Zero for Number {
- #[inline]
- fn zero() -> Self {
- Self::new(0.)
- }
-
- #[inline]
- fn is_zero(&self) -> bool {
- self.get() == 0.
- }
-}
-
-impl From<Number> for f32 {
- #[inline]
- fn from(n: Number) -> Self {
- n.get()
- }
-}
-
-impl From<Number> for f64 {
- #[inline]
- fn from(n: Number) -> Self {
- n.get() as f64
- }
-}
-
-/// A Number which is >= 0.0.
-pub type NonNegativeNumber = NonNegative<Number>;
-
-impl Parse for NonNegativeNumber {
- fn parse<'i, 't>(
- context: &ParserContext,
- input: &mut Parser<'i, 't>,
- ) -> Result<Self, ParseError<'i>> {
- parse_number_with_clamping_mode(context, input, AllowedNumericType::NonNegative)
- .map(NonNegative::<Number>)
- }
-}
-
-impl One for NonNegativeNumber {
- #[inline]
- fn one() -> Self {
- NonNegativeNumber::new(1.0)
- }
-
- #[inline]
- fn is_one(&self) -> bool {
- self.get() == 1.0
- }
-}
-
-impl NonNegativeNumber {
- /// Returns a new non-negative number with the value `val`.
- pub fn new(val: CSSFloat) -> Self {
- NonNegative::<Number>(Number::new(val.max(0.)))
- }
-
- /// Returns the numeric value.
- #[inline]
- pub fn get(&self) -> f32 {
- self.0.get()
- }
-}
-
-/// An Integer which is >= 0.
-pub type NonNegativeInteger = NonNegative<Integer>;
-
-impl Parse for NonNegativeInteger {
- fn parse<'i, 't>(
- context: &ParserContext,
- input: &mut Parser<'i, 't>,
- ) -> Result<Self, ParseError<'i>> {
- Ok(NonNegative(Integer::parse_non_negative(context, input)?))
- }
-}
-
-/// A Number which is >= 1.0.
-pub type GreaterThanOrEqualToOneNumber = GreaterThanOrEqualToOne<Number>;
-
-impl Parse for GreaterThanOrEqualToOneNumber {
- fn parse<'i, 't>(
- context: &ParserContext,
- input: &mut Parser<'i, 't>,
- ) -> Result<Self, ParseError<'i>> {
- parse_number_with_clamping_mode(context, input, AllowedNumericType::AtLeastOne)
- .map(GreaterThanOrEqualToOne::<Number>)
- }
-}
-
-/// <number> | <percentage>
-///
-/// Accepts only non-negative numbers.
-#[allow(missing_docs)]
-#[derive(Clone, Copy, Debug, MallocSizeOf, PartialEq, SpecifiedValueInfo, ToCss, ToShmem)]
-pub enum NumberOrPercentage {
- Percentage(Percentage),
- Number(Number),
-}
-
-impl NumberOrPercentage {
- fn parse_with_clamping_mode<'i, 't>(
- context: &ParserContext,
- input: &mut Parser<'i, 't>,
- type_: AllowedNumericType,
- ) -> Result<Self, ParseError<'i>> {
- if let Ok(per) =
- input.try_parse(|i| Percentage::parse_with_clamping_mode(context, i, type_))
- {
- return Ok(NumberOrPercentage::Percentage(per));
- }
-
- parse_number_with_clamping_mode(context, input, type_).map(NumberOrPercentage::Number)
- }
-
- /// Parse a non-negative number or percentage.
- pub fn parse_non_negative<'i, 't>(
- context: &ParserContext,
- input: &mut Parser<'i, 't>,
- ) -> Result<Self, ParseError<'i>> {
- Self::parse_with_clamping_mode(context, input, AllowedNumericType::NonNegative)
- }
-
- /// Convert the number or the percentage to a number.
- pub fn to_percentage(self) -> Percentage {
- match self {
- Self::Percentage(p) => p,
- Self::Number(n) => n.to_percentage(),
- }
- }
-
- /// Convert the number or the percentage to a number.
- pub fn to_number(self) -> Number {
- match self {
- Self::Percentage(p) => p.to_number(),
- Self::Number(n) => n,
- }
- }
-}
-
-impl Parse for NumberOrPercentage {
- fn parse<'i, 't>(
- context: &ParserContext,
- input: &mut Parser<'i, 't>,
- ) -> Result<Self, ParseError<'i>> {
- Self::parse_with_clamping_mode(context, input, AllowedNumericType::All)
- }
-}
-
-/// A non-negative <number> | <percentage>.
-pub type NonNegativeNumberOrPercentage = NonNegative<NumberOrPercentage>;
-
-impl NonNegativeNumberOrPercentage {
- /// Returns the `100%` value.
- #[inline]
- pub fn hundred_percent() -> Self {
- NonNegative(NumberOrPercentage::Percentage(Percentage::hundred()))
- }
-}
-
-impl Parse for NonNegativeNumberOrPercentage {
- fn parse<'i, 't>(
- context: &ParserContext,
- input: &mut Parser<'i, 't>,
- ) -> Result<Self, ParseError<'i>> {
- Ok(NonNegative(NumberOrPercentage::parse_non_negative(
- context, input,
- )?))
- }
-}
-
-/// The value of Opacity is <alpha-value>, which is "<number> | <percentage>".
-/// However, we serialize the specified value as number, so it's ok to store
-/// the Opacity as Number.
-#[derive(
- Clone, Copy, Debug, MallocSizeOf, PartialEq, PartialOrd, SpecifiedValueInfo, ToCss, ToShmem,
-)]
-pub struct Opacity(Number);
-
-impl Parse for Opacity {
- /// Opacity accepts <number> | <percentage>, so we parse it as NumberOrPercentage,
- /// and then convert into an Number if it's a Percentage.
- /// https://drafts.csswg.org/cssom/#serializing-css-values
- fn parse<'i, 't>(
- context: &ParserContext,
- input: &mut Parser<'i, 't>,
- ) -> Result<Self, ParseError<'i>> {
- let number = NumberOrPercentage::parse(context, input)?.to_number();
- Ok(Opacity(number))
- }
-}
-
-impl ToComputedValue for Opacity {
- type ComputedValue = CSSFloat;
-
- #[inline]
- fn to_computed_value(&self, context: &Context) -> CSSFloat {
- let value = self.0.to_computed_value(context);
- if context.for_smil_animation {
- // SMIL expects to be able to interpolate between out-of-range
- // opacity values.
- value
- } else {
- value.min(1.0).max(0.0)
- }
- }
-
- #[inline]
- fn from_computed_value(computed: &CSSFloat) -> Self {
- Opacity(Number::from_computed_value(computed))
- }
-}
-
-/// A specified `<integer>`, optionally coming from a `calc()` expression.
-///
-/// <https://drafts.csswg.org/css-values/#integers>
-#[derive(Clone, Copy, Debug, Eq, MallocSizeOf, PartialEq, PartialOrd, ToShmem)]
-pub struct Integer {
- value: CSSInteger,
- was_calc: bool,
-}
-
-impl Zero for Integer {
- #[inline]
- fn zero() -> Self {
- Self::new(0)
- }
-
- #[inline]
- fn is_zero(&self) -> bool {
- self.value() == 0
- }
-}
-
-impl One for Integer {
- #[inline]
- fn one() -> Self {
- Self::new(1)
- }
-
- #[inline]
- fn is_one(&self) -> bool {
- self.value() == 1
- }
-}
-
-impl PartialEq<i32> for Integer {
- fn eq(&self, value: &i32) -> bool {
- self.value() == *value
- }
-}
-
-impl Integer {
- /// Trivially constructs a new `Integer` value.
- pub fn new(val: CSSInteger) -> Self {
- Integer {
- value: val,
- was_calc: false,
- }
- }
-
- /// Returns the integer value associated with this value.
- pub fn value(&self) -> CSSInteger {
- self.value
- }
-
- /// Trivially constructs a new integer value from a `calc()` expression.
- fn from_calc(val: CSSInteger) -> Self {
- Integer {
- value: val,
- was_calc: true,
- }
- }
-}
-
-impl Parse for Integer {
- fn parse<'i, 't>(
- context: &ParserContext,
- input: &mut Parser<'i, 't>,
- ) -> Result<Self, ParseError<'i>> {
- let location = input.current_source_location();
- match *input.next()? {
- Token::Number {
- int_value: Some(v), ..
- } => Ok(Integer::new(v)),
- Token::Function(ref name) => {
- let function = CalcNode::math_function(context, name, location)?;
- let result = CalcNode::parse_integer(context, input, function)?;
- Ok(Integer::from_calc(result))
- },
- ref t => Err(location.new_unexpected_token_error(t.clone())),
- }
- }
-}
-
-impl Integer {
- /// Parse an integer value which is at least `min`.
- pub fn parse_with_minimum<'i, 't>(
- context: &ParserContext,
- input: &mut Parser<'i, 't>,
- min: i32,
- ) -> Result<Integer, ParseError<'i>> {
- let value = Integer::parse(context, input)?;
- // FIXME(emilio): The spec asks us to avoid rejecting it at parse
- // time except until computed value time.
- //
- // It's not totally clear it's worth it though, and no other browser
- // does this.
- if value.value() < min {
- return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError));
- }
- Ok(value)
- }
-
- /// Parse a non-negative integer.
- pub fn parse_non_negative<'i, 't>(
- context: &ParserContext,
- input: &mut Parser<'i, 't>,
- ) -> Result<Integer, ParseError<'i>> {
- Integer::parse_with_minimum(context, input, 0)
- }
-
- /// Parse a positive integer (>= 1).
- pub fn parse_positive<'i, 't>(
- context: &ParserContext,
- input: &mut Parser<'i, 't>,
- ) -> Result<Integer, ParseError<'i>> {
- Integer::parse_with_minimum(context, input, 1)
- }
-}
-
-impl ToComputedValue for Integer {
- type ComputedValue = i32;
-
- #[inline]
- fn to_computed_value(&self, _: &Context) -> i32 {
- self.value
- }
-
- #[inline]
- fn from_computed_value(computed: &i32) -> Self {
- Integer::new(*computed)
- }
-}
-
-impl ToCss for Integer {
- fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
- where
- W: Write,
- {
- if self.was_calc {
- dest.write_str("calc(")?;
- }
- self.value.to_css(dest)?;
- if self.was_calc {
- dest.write_char(')')?;
- }
- Ok(())
- }
-}
-
-impl SpecifiedValueInfo for Integer {}
-
-/// A wrapper of Integer, with value >= 1.
-pub type PositiveInteger = GreaterThanOrEqualToOne<Integer>;
-
-impl Parse for PositiveInteger {
- #[inline]
- fn parse<'i, 't>(
- context: &ParserContext,
- input: &mut Parser<'i, 't>,
- ) -> Result<Self, ParseError<'i>> {
- Integer::parse_positive(context, input).map(GreaterThanOrEqualToOne)
- }
-}
-
-/// The specified value of a grid `<track-breadth>`
-pub type TrackBreadth = GenericTrackBreadth<LengthPercentage>;
-
-/// The specified value of a grid `<track-size>`
-pub type TrackSize = GenericTrackSize<LengthPercentage>;
-
-/// The specified value of a grid `<track-size>+`
-pub type ImplicitGridTracks = GenericImplicitGridTracks<TrackSize>;
-
-/// The specified value of a grid `<track-list>`
-/// (could also be `<auto-track-list>` or `<explicit-track-list>`)
-pub type TrackList = GenericTrackList<LengthPercentage, Integer>;
-
-/// The specified value of a `<grid-line>`.
-pub type GridLine = GenericGridLine<Integer>;
-
-/// `<grid-template-rows> | <grid-template-columns>`
-pub type GridTemplateComponent = GenericGridTemplateComponent<LengthPercentage, Integer>;
-
-/// rect(...)
-pub type ClipRect = generics::GenericClipRect<LengthOrAuto>;
-
-impl Parse for ClipRect {
- fn parse<'i, 't>(
- context: &ParserContext,
- input: &mut Parser<'i, 't>,
- ) -> Result<Self, ParseError<'i>> {
- Self::parse_quirky(context, input, AllowQuirks::No)
- }
-}
-
-impl ClipRect {
- /// Parses a rect(<top>, <left>, <bottom>, <right>), allowing quirks.
- fn parse_quirky<'i, 't>(
- context: &ParserContext,
- input: &mut Parser<'i, 't>,
- allow_quirks: AllowQuirks,
- ) -> Result<Self, ParseError<'i>> {
- input.expect_function_matching("rect")?;
-
- fn parse_argument<'i, 't>(
- context: &ParserContext,
- input: &mut Parser<'i, 't>,
- allow_quirks: AllowQuirks,
- ) -> Result<LengthOrAuto, ParseError<'i>> {
- LengthOrAuto::parse_quirky(context, input, allow_quirks)
- }
-
- input.parse_nested_block(|input| {
- let top = parse_argument(context, input, allow_quirks)?;
- let right;
- let bottom;
- let left;
-
- if input.try_parse(|input| input.expect_comma()).is_ok() {
- right = parse_argument(context, input, allow_quirks)?;
- input.expect_comma()?;
- bottom = parse_argument(context, input, allow_quirks)?;
- input.expect_comma()?;
- left = parse_argument(context, input, allow_quirks)?;
- } else {
- right = parse_argument(context, input, allow_quirks)?;
- bottom = parse_argument(context, input, allow_quirks)?;
- left = parse_argument(context, input, allow_quirks)?;
- }
-
- Ok(ClipRect {
- top,
- right,
- bottom,
- left,
- })
- })
- }
-}
-
-/// rect(...) | auto
-pub type ClipRectOrAuto = generics::GenericClipRectOrAuto<ClipRect>;
-
-impl ClipRectOrAuto {
- /// Parses a ClipRect or Auto, allowing quirks.
- pub fn parse_quirky<'i, 't>(
- context: &ParserContext,
- input: &mut Parser<'i, 't>,
- allow_quirks: AllowQuirks,
- ) -> Result<Self, ParseError<'i>> {
- if let Ok(v) = input.try_parse(|i| ClipRect::parse_quirky(context, i, allow_quirks)) {
- return Ok(generics::GenericClipRectOrAuto::Rect(v));
- }
- input.expect_ident_matching("auto")?;
- Ok(generics::GenericClipRectOrAuto::Auto)
- }
-}
-
-/// Whether quirks are allowed in this context.
-#[derive(Clone, Copy, PartialEq)]
-pub enum AllowQuirks {
- /// Quirks are not allowed.
- No,
- /// Quirks are allowed, in quirks mode.
- Yes,
- /// Quirks are always allowed, used for SVG lengths.
- Always,
-}
-
-impl AllowQuirks {
- /// Returns `true` if quirks are allowed in this context.
- pub fn allowed(self, quirks_mode: QuirksMode) -> bool {
- match self {
- AllowQuirks::Always => true,
- AllowQuirks::No => false,
- AllowQuirks::Yes => quirks_mode == QuirksMode::Quirks,
- }
- }
-}
-
-/// An attr(...) rule
-///
-/// `[namespace? `|`]? ident`
-#[derive(
- Clone,
- Debug,
- Eq,
- MallocSizeOf,
- PartialEq,
- SpecifiedValueInfo,
- ToComputedValue,
- ToResolvedValue,
- ToShmem,
-)]
-#[css(function)]
-#[repr(C)]
-pub struct Attr {
- /// Optional namespace prefix.
- pub namespace_prefix: Prefix,
- /// Optional namespace URL.
- pub namespace_url: Namespace,
- /// Attribute name
- pub attribute: Atom,
-}
-
-impl Parse for Attr {
- fn parse<'i, 't>(
- context: &ParserContext,
- input: &mut Parser<'i, 't>,
- ) -> Result<Attr, ParseError<'i>> {
- input.expect_function_matching("attr")?;
- input.parse_nested_block(|i| Attr::parse_function(context, i))
- }
-}
-
-/// Get the Namespace for a given prefix from the namespace map.
-fn get_namespace_for_prefix(prefix: &Prefix, context: &ParserContext) -> Option<Namespace> {
- context.namespaces.prefixes.get(prefix).cloned()
-}
-
-impl Attr {
- /// Parse contents of attr() assuming we have already parsed `attr` and are
- /// within a parse_nested_block()
- pub fn parse_function<'i, 't>(
- context: &ParserContext,
- input: &mut Parser<'i, 't>,
- ) -> Result<Attr, ParseError<'i>> {
- // Syntax is `[namespace? `|`]? ident`
- // no spaces allowed
- let first = input.try_parse(|i| i.expect_ident_cloned()).ok();
- if let Ok(token) = input.try_parse(|i| i.next_including_whitespace().map(|t| t.clone())) {
- match token {
- Token::Delim('|') => {
- let location = input.current_source_location();
- // must be followed by an ident
- let second_token = match *input.next_including_whitespace()? {
- Token::Ident(ref second) => second,
- ref t => return Err(location.new_unexpected_token_error(t.clone())),
- };
-
- let (namespace_prefix, namespace_url) = if let Some(ns) = first {
- let prefix = Prefix::from(ns.as_ref());
- let ns = match get_namespace_for_prefix(&prefix, context) {
- Some(ns) => ns,
- None => {
- return Err(location
- .new_custom_error(StyleParseErrorKind::UnspecifiedError));
- },
- };
- (prefix, ns)
- } else {
- (Prefix::default(), Namespace::default())
- };
- return Ok(Attr {
- namespace_prefix,
- namespace_url,
- attribute: Atom::from(second_token.as_ref()),
- });
- },
- // In the case of attr(foobar ) we don't want to error out
- // because of the trailing whitespace.
- Token::WhiteSpace(..) => {},
- ref t => return Err(input.new_unexpected_token_error(t.clone())),
- }
- }
-
- if let Some(first) = first {
- Ok(Attr {
- namespace_prefix: Prefix::default(),
- namespace_url: Namespace::default(),
- attribute: Atom::from(first.as_ref()),
- })
- } else {
- Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError))
- }
- }
-}
-
-impl ToCss for Attr {
- fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
- where
- W: Write,
- {
- dest.write_str("attr(")?;
- if !self.namespace_prefix.is_empty() {
- serialize_atom_identifier(&self.namespace_prefix, dest)?;
- dest.write_char('|')?;
- }
- serialize_atom_identifier(&self.attribute, dest)?;
- dest.write_char(')')
- }
-}
diff --git a/components/style/values/specified/motion.rs b/components/style/values/specified/motion.rs
deleted file mode 100644
index c1ace2cea60..00000000000
--- a/components/style/values/specified/motion.rs
+++ /dev/null
@@ -1,238 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
-
-//! Specified types for CSS values that are related to motion path.
-
-use crate::parser::{Parse, ParserContext};
-use crate::values::computed::motion::OffsetRotate as ComputedOffsetRotate;
-use crate::values::computed::{Context, ToComputedValue};
-use crate::values::generics::motion as generics;
-use crate::values::specified::position::{HorizontalPosition, VerticalPosition};
-use crate::values::specified::{Angle, Position};
-use crate::Zero;
-use cssparser::Parser;
-use style_traits::{ParseError, StyleParseErrorKind};
-
-/// The specified value of ray() function.
-pub type RayFunction = generics::GenericRayFunction<Angle, Position>;
-
-/// The specified value of `offset-path`.
-pub type OffsetPath = generics::GenericOffsetPath<RayFunction>;
-
-/// The specified value of `offset-position`.
-pub type OffsetPosition = generics::GenericOffsetPosition<HorizontalPosition, VerticalPosition>;
-
-impl Parse for RayFunction {
- fn parse<'i, 't>(
- context: &ParserContext,
- input: &mut Parser<'i, 't>,
- ) -> Result<Self, ParseError<'i>> {
- use crate::values::specified::PositionOrAuto;
-
- if !static_prefs::pref!("layout.css.motion-path-ray.enabled") {
- return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError));
- }
-
- let mut angle = None;
- let mut size = None;
- let mut contain = false;
- let mut position = None;
- loop {
- if angle.is_none() {
- angle = input.try_parse(|i| Angle::parse(context, i)).ok();
- }
-
- if size.is_none() {
- size = input.try_parse(generics::RaySize::parse).ok();
- if size.is_some() {
- continue;
- }
- }
-
- if !contain {
- contain = input
- .try_parse(|i| i.expect_ident_matching("contain"))
- .is_ok();
- if contain {
- continue;
- }
- }
-
- if position.is_none() {
- if input.try_parse(|i| i.expect_ident_matching("at")).is_ok() {
- let pos = Position::parse(context, input)?;
- position = Some(PositionOrAuto::Position(pos));
- }
-
- if position.is_some() {
- continue;
- }
- }
- break;
- }
-
- if angle.is_none() {
- return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError));
- }
-
- Ok(RayFunction {
- angle: angle.unwrap(),
- // If no <ray-size> is specified it defaults to closest-side.
- size: size.unwrap_or(generics::RaySize::ClosestSide),
- contain,
- position: position.unwrap_or(PositionOrAuto::auto()),
- })
- }
-}
-
-impl Parse for OffsetPath {
- fn parse<'i, 't>(
- context: &ParserContext,
- input: &mut Parser<'i, 't>,
- ) -> Result<Self, ParseError<'i>> {
- use crate::values::specified::svg_path::{AllowEmpty, SVGPathData};
-
- // Parse none.
- if input.try_parse(|i| i.expect_ident_matching("none")).is_ok() {
- return Ok(OffsetPath::none());
- }
-
- // Parse possible functions.
- let location = input.current_source_location();
- let function = input.expect_function()?.clone();
- input.parse_nested_block(move |i| {
- match_ignore_ascii_case! { &function,
- // Bug 1186329: Implement the parser for <basic-shape>, <geometry-box>,
- // and <url>.
- "path" => SVGPathData::parse(i, AllowEmpty::No).map(OffsetPath::Path),
- "ray" => RayFunction::parse(context, i).map(|v| OffsetPath::Ray(Box::new(v))),
- _ => {
- Err(location.new_custom_error(
- StyleParseErrorKind::UnexpectedFunction(function.clone())
- ))
- },
- }
- })
- }
-}
-
-/// The direction of offset-rotate.
-#[derive(
- Clone, Copy, Debug, MallocSizeOf, Parse, PartialEq, SpecifiedValueInfo, ToCss, ToShmem,
-)]
-#[repr(u8)]
-pub enum OffsetRotateDirection {
- /// Unspecified direction keyword.
- #[css(skip)]
- None,
- /// 0deg offset (face forward).
- Auto,
- /// 180deg offset (face backward).
- Reverse,
-}
-
-impl OffsetRotateDirection {
- /// Returns true if it is none (i.e. the keyword is not specified).
- #[inline]
- fn is_none(&self) -> bool {
- *self == OffsetRotateDirection::None
- }
-}
-
-#[inline]
-fn direction_specified_and_angle_is_zero(direction: &OffsetRotateDirection, angle: &Angle) -> bool {
- !direction.is_none() && angle.is_zero()
-}
-
-/// The specified offset-rotate.
-/// The syntax is: "[ auto | reverse ] || <angle>"
-///
-/// https://drafts.fxtf.org/motion-1/#offset-rotate-property
-#[derive(Clone, Copy, Debug, MallocSizeOf, PartialEq, SpecifiedValueInfo, ToCss, ToShmem)]
-pub struct OffsetRotate {
- /// [auto | reverse].
- #[css(skip_if = "OffsetRotateDirection::is_none")]
- direction: OffsetRotateDirection,
- /// <angle>.
- /// If direction is None, this is a fixed angle which indicates a
- /// constant clockwise rotation transformation applied to it by this
- /// specified rotation angle. Otherwise, the angle will be added to
- /// the angle of the direction in layout.
- #[css(contextual_skip_if = "direction_specified_and_angle_is_zero")]
- angle: Angle,
-}
-
-impl OffsetRotate {
- /// Returns the initial value, auto.
- #[inline]
- pub fn auto() -> Self {
- OffsetRotate {
- direction: OffsetRotateDirection::Auto,
- angle: Angle::zero(),
- }
- }
-
- /// Returns true if self is auto 0deg.
- #[inline]
- pub fn is_auto(&self) -> bool {
- self.direction == OffsetRotateDirection::Auto && self.angle.is_zero()
- }
-}
-
-impl Parse for OffsetRotate {
- fn parse<'i, 't>(
- context: &ParserContext,
- input: &mut Parser<'i, 't>,
- ) -> Result<Self, ParseError<'i>> {
- let location = input.current_source_location();
- let mut direction = input.try_parse(OffsetRotateDirection::parse);
- let angle = input.try_parse(|i| Angle::parse(context, i));
- if direction.is_err() {
- // The direction and angle could be any order, so give it a change to parse
- // direction again.
- direction = input.try_parse(OffsetRotateDirection::parse);
- }
-
- if direction.is_err() && angle.is_err() {
- return Err(location.new_custom_error(StyleParseErrorKind::UnspecifiedError));
- }
-
- Ok(OffsetRotate {
- direction: direction.unwrap_or(OffsetRotateDirection::None),
- angle: angle.unwrap_or(Zero::zero()),
- })
- }
-}
-
-impl ToComputedValue for OffsetRotate {
- type ComputedValue = ComputedOffsetRotate;
-
- #[inline]
- fn to_computed_value(&self, context: &Context) -> Self::ComputedValue {
- use crate::values::computed::Angle as ComputedAngle;
-
- ComputedOffsetRotate {
- auto: !self.direction.is_none(),
- angle: if self.direction == OffsetRotateDirection::Reverse {
- // The computed value should always convert "reverse" into "auto".
- // e.g. "reverse calc(20deg + 10deg)" => "auto 210deg"
- self.angle.to_computed_value(context) + ComputedAngle::from_degrees(180.0)
- } else {
- self.angle.to_computed_value(context)
- },
- }
- }
-
- #[inline]
- fn from_computed_value(computed: &Self::ComputedValue) -> Self {
- OffsetRotate {
- direction: if computed.auto {
- OffsetRotateDirection::Auto
- } else {
- OffsetRotateDirection::None
- },
- angle: ToComputedValue::from_computed_value(&computed.angle),
- }
- }
-}
diff --git a/components/style/values/specified/outline.rs b/components/style/values/specified/outline.rs
deleted file mode 100644
index 6e5382d4c2b..00000000000
--- a/components/style/values/specified/outline.rs
+++ /dev/null
@@ -1,71 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
-
-//! Specified values for outline properties
-
-use crate::parser::{Parse, ParserContext};
-use crate::values::specified::BorderStyle;
-use cssparser::Parser;
-use selectors::parser::SelectorParseErrorKind;
-use style_traits::ParseError;
-
-#[derive(
- Clone,
- Copy,
- Debug,
- Eq,
- MallocSizeOf,
- Ord,
- PartialEq,
- PartialOrd,
- SpecifiedValueInfo,
- ToComputedValue,
- ToCss,
- ToResolvedValue,
- ToShmem,
-)]
-#[repr(C, u8)]
-/// <https://drafts.csswg.org/css-ui/#propdef-outline-style>
-pub enum OutlineStyle {
- /// auto
- Auto,
- /// <border-style>
- BorderStyle(BorderStyle),
-}
-
-impl OutlineStyle {
- #[inline]
- /// Get default value as None
- pub fn none() -> OutlineStyle {
- OutlineStyle::BorderStyle(BorderStyle::None)
- }
-
- #[inline]
- /// Get value for None or Hidden
- pub fn none_or_hidden(&self) -> bool {
- match *self {
- OutlineStyle::Auto => false,
- OutlineStyle::BorderStyle(ref style) => style.none_or_hidden(),
- }
- }
-}
-
-impl Parse for OutlineStyle {
- fn parse<'i, 't>(
- _context: &ParserContext,
- input: &mut Parser<'i, 't>,
- ) -> Result<OutlineStyle, ParseError<'i>> {
- if let Ok(border_style) = input.try_parse(BorderStyle::parse) {
- if let BorderStyle::Hidden = border_style {
- return Err(input
- .new_custom_error(SelectorParseErrorKind::UnexpectedIdent("hidden".into())));
- }
-
- return Ok(OutlineStyle::BorderStyle(border_style));
- }
-
- input.expect_ident_matching("auto")?;
- Ok(OutlineStyle::Auto)
- }
-}
diff --git a/components/style/values/specified/page.rs b/components/style/values/specified/page.rs
deleted file mode 100644
index 76d9105e8f3..00000000000
--- a/components/style/values/specified/page.rs
+++ /dev/null
@@ -1,99 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
-
-//! Specified @page at-rule properties and named-page style properties
-
-use crate::parser::{Parse, ParserContext};
-use crate::values::generics::size::Size2D;
-use crate::values::specified::length::NonNegativeLength;
-use crate::values::{generics, CustomIdent};
-use cssparser::Parser;
-use style_traits::ParseError;
-
-pub use generics::page::PageOrientation;
-pub use generics::page::PageSizeOrientation;
-pub use generics::page::PaperSize;
-/// Specified value of the @page size descriptor
-pub type PageSize = generics::page::PageSize<Size2D<NonNegativeLength>>;
-
-impl Parse for PageSize {
- fn parse<'i, 't>(
- context: &ParserContext,
- input: &mut Parser<'i, 't>,
- ) -> Result<Self, ParseError<'i>> {
- // Try to parse as <page-size> [ <orientation> ]
- if let Ok(paper_size) = input.try_parse(PaperSize::parse) {
- let orientation = input
- .try_parse(PageSizeOrientation::parse)
- .unwrap_or(PageSizeOrientation::Portrait);
- return Ok(PageSize::PaperSize(paper_size, orientation));
- }
- // Try to parse as <orientation> [ <page-size> ]
- if let Ok(orientation) = input.try_parse(PageSizeOrientation::parse) {
- if let Ok(paper_size) = input.try_parse(PaperSize::parse) {
- return Ok(PageSize::PaperSize(paper_size, orientation));
- }
- return Ok(PageSize::Orientation(orientation));
- }
- // Try to parse dimensions
- if let Ok(size) =
- input.try_parse(|i| Size2D::parse_with(context, i, NonNegativeLength::parse))
- {
- return Ok(PageSize::Size(size));
- }
- // auto value
- input.expect_ident_matching("auto")?;
- Ok(PageSize::Auto)
- }
-}
-
-/// Page name value.
-///
-/// https://drafts.csswg.org/css-page-3/#using-named-pages
-#[derive(
- Clone,
- Debug,
- MallocSizeOf,
- PartialEq,
- SpecifiedValueInfo,
- ToCss,
- ToComputedValue,
- ToResolvedValue,
- ToShmem,
-)]
-#[repr(C, u8)]
-pub enum PageName {
- /// `auto` value.
- Auto,
- /// Page name value
- PageName(CustomIdent),
-}
-
-impl Parse for PageName {
- fn parse<'i, 't>(
- _context: &ParserContext,
- input: &mut Parser<'i, 't>,
- ) -> Result<Self, ParseError<'i>> {
- let location = input.current_source_location();
- let ident = input.expect_ident()?;
- Ok(match_ignore_ascii_case! { ident,
- "auto" => PageName::auto(),
- _ => PageName::PageName(CustomIdent::from_ident(location, ident, &[])?),
- })
- }
-}
-
-impl PageName {
- /// `auto` value.
- #[inline]
- pub fn auto() -> Self {
- PageName::Auto
- }
-
- /// Whether this is the `auto` value.
- #[inline]
- pub fn is_auto(&self) -> bool {
- matches!(*self, PageName::Auto)
- }
-}
diff --git a/components/style/values/specified/percentage.rs b/components/style/values/specified/percentage.rs
deleted file mode 100644
index ccf16d64639..00000000000
--- a/components/style/values/specified/percentage.rs
+++ /dev/null
@@ -1,225 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
-
-//! Specified percentages.
-
-use crate::parser::{Parse, ParserContext};
-use crate::values::computed::percentage::Percentage as ComputedPercentage;
-use crate::values::computed::{Context, ToComputedValue};
-use crate::values::generics::NonNegative;
-use crate::values::specified::calc::CalcNode;
-use crate::values::specified::Number;
-use crate::values::{normalize, serialize_percentage, CSSFloat};
-use cssparser::{Parser, Token};
-use std::fmt::{self, Write};
-use style_traits::values::specified::AllowedNumericType;
-use style_traits::{CssWriter, ParseError, SpecifiedValueInfo, ToCss};
-
-/// A percentage value.
-#[derive(Clone, Copy, Debug, Default, MallocSizeOf, PartialEq, ToShmem)]
-pub struct Percentage {
- /// The percentage value as a float.
- ///
- /// [0 .. 100%] maps to [0.0 .. 1.0]
- value: CSSFloat,
- /// If this percentage came from a calc() expression, this tells how
- /// clamping should be done on the value.
- calc_clamping_mode: Option<AllowedNumericType>,
-}
-
-impl ToCss for Percentage {
- fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
- where
- W: Write,
- {
- if self.calc_clamping_mode.is_some() {
- dest.write_str("calc(")?;
- }
-
- serialize_percentage(self.value, dest)?;
-
- if self.calc_clamping_mode.is_some() {
- dest.write_char(')')?;
- }
- Ok(())
- }
-}
-
-impl Percentage {
- /// Creates a percentage from a numeric value.
- pub(super) fn new_with_clamping_mode(
- value: CSSFloat,
- calc_clamping_mode: Option<AllowedNumericType>,
- ) -> Self {
- Self {
- value,
- calc_clamping_mode,
- }
- }
-
- /// Creates a percentage from a numeric value.
- pub fn new(value: CSSFloat) -> Self {
- Self::new_with_clamping_mode(value, None)
- }
-
- /// `0%`
- #[inline]
- pub fn zero() -> Self {
- Percentage {
- value: 0.,
- calc_clamping_mode: None,
- }
- }
-
- /// `100%`
- #[inline]
- pub fn hundred() -> Self {
- Percentage {
- value: 1.,
- calc_clamping_mode: None,
- }
- }
-
- /// Gets the underlying value for this float.
- pub fn get(&self) -> CSSFloat {
- self.calc_clamping_mode
- .map_or(self.value, |mode| mode.clamp(self.value))
- }
-
- /// Returns this percentage as a number.
- pub fn to_number(&self) -> Number {
- Number::new_with_clamping_mode(self.value, self.calc_clamping_mode)
- }
-
- /// Returns the calc() clamping mode for this percentage.
- pub fn calc_clamping_mode(&self) -> Option<AllowedNumericType> {
- self.calc_clamping_mode
- }
-
- /// Reverses this percentage, preserving calc-ness.
- ///
- /// For example: If it was 20%, convert it into 80%.
- pub fn reverse(&mut self) {
- let new_value = 1. - self.value;
- self.value = new_value;
- }
-
- /// Parses a specific kind of percentage.
- pub fn parse_with_clamping_mode<'i, 't>(
- context: &ParserContext,
- input: &mut Parser<'i, 't>,
- num_context: AllowedNumericType,
- ) -> Result<Self, ParseError<'i>> {
- let location = input.current_source_location();
- match *input.next()? {
- Token::Percentage { unit_value, .. }
- if num_context.is_ok(context.parsing_mode, unit_value) =>
- {
- Ok(Percentage::new(unit_value))
- },
- Token::Function(ref name) => {
- let function = CalcNode::math_function(context, name, location)?;
- let value = CalcNode::parse_percentage(context, input, function)?;
- Ok(Percentage {
- value,
- calc_clamping_mode: Some(num_context),
- })
- },
- ref t => Err(location.new_unexpected_token_error(t.clone())),
- }
- }
-
- /// Parses a percentage token, but rejects it if it's negative.
- pub fn parse_non_negative<'i, 't>(
- context: &ParserContext,
- input: &mut Parser<'i, 't>,
- ) -> Result<Self, ParseError<'i>> {
- Self::parse_with_clamping_mode(context, input, AllowedNumericType::NonNegative)
- }
-
- /// Parses a percentage token, but rejects it if it's negative or more than
- /// 100%.
- pub fn parse_zero_to_a_hundred<'i, 't>(
- context: &ParserContext,
- input: &mut Parser<'i, 't>,
- ) -> Result<Self, ParseError<'i>> {
- Self::parse_with_clamping_mode(context, input, AllowedNumericType::ZeroToOne)
- }
-
- /// Clamp to 100% if the value is over 100%.
- #[inline]
- pub fn clamp_to_hundred(self) -> Self {
- Percentage {
- value: self.value.min(1.),
- calc_clamping_mode: self.calc_clamping_mode,
- }
- }
-}
-
-impl Parse for Percentage {
- #[inline]
- fn parse<'i, 't>(
- context: &ParserContext,
- input: &mut Parser<'i, 't>,
- ) -> Result<Self, ParseError<'i>> {
- Self::parse_with_clamping_mode(context, input, AllowedNumericType::All)
- }
-}
-
-impl ToComputedValue for Percentage {
- type ComputedValue = ComputedPercentage;
-
- #[inline]
- fn to_computed_value(&self, _: &Context) -> Self::ComputedValue {
- ComputedPercentage(normalize(self.get()))
- }
-
- #[inline]
- fn from_computed_value(computed: &Self::ComputedValue) -> Self {
- Percentage::new(computed.0)
- }
-}
-
-impl SpecifiedValueInfo for Percentage {}
-
-/// Turns the percentage into a plain float.
-pub trait ToPercentage {
- /// Returns whether this percentage used to be a calc().
- fn is_calc(&self) -> bool {
- false
- }
- /// Turns the percentage into a plain float.
- fn to_percentage(&self) -> CSSFloat;
-}
-
-impl ToPercentage for Percentage {
- fn is_calc(&self) -> bool {
- self.calc_clamping_mode.is_some()
- }
-
- fn to_percentage(&self) -> CSSFloat {
- self.get()
- }
-}
-
-/// A wrapper of Percentage, whose value must be >= 0.
-pub type NonNegativePercentage = NonNegative<Percentage>;
-
-impl Parse for NonNegativePercentage {
- #[inline]
- fn parse<'i, 't>(
- context: &ParserContext,
- input: &mut Parser<'i, 't>,
- ) -> Result<Self, ParseError<'i>> {
- Ok(NonNegative(Percentage::parse_non_negative(context, input)?))
- }
-}
-
-impl NonNegativePercentage {
- /// Convert to ComputedPercentage, for FontFaceRule size-adjust getter.
- #[inline]
- pub fn compute(&self) -> ComputedPercentage {
- ComputedPercentage(self.0.get())
- }
-}
diff --git a/components/style/values/specified/position.rs b/components/style/values/specified/position.rs
deleted file mode 100644
index 8f4b495d3f6..00000000000
--- a/components/style/values/specified/position.rs
+++ /dev/null
@@ -1,905 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
-
-//! CSS handling for the specified value of
-//! [`position`][position]s
-//!
-//! [position]: https://drafts.csswg.org/css-backgrounds-3/#position
-
-use crate::parser::{Parse, ParserContext};
-use crate::selector_map::PrecomputedHashMap;
-use crate::str::HTML_SPACE_CHARACTERS;
-use crate::values::computed::LengthPercentage as ComputedLengthPercentage;
-use crate::values::computed::{Context, Percentage, ToComputedValue};
-use crate::values::generics::position::AspectRatio as GenericAspectRatio;
-use crate::values::generics::position::Position as GenericPosition;
-use crate::values::generics::position::PositionComponent as GenericPositionComponent;
-use crate::values::generics::position::PositionOrAuto as GenericPositionOrAuto;
-use crate::values::generics::position::ZIndex as GenericZIndex;
-use crate::values::specified::{AllowQuirks, Integer, LengthPercentage, NonNegativeNumber};
-use crate::{Atom, Zero};
-use cssparser::Parser;
-use selectors::parser::SelectorParseErrorKind;
-use servo_arc::Arc;
-use std::fmt::{self, Write};
-use style_traits::values::specified::AllowedNumericType;
-use style_traits::{CssWriter, ParseError, StyleParseErrorKind, ToCss};
-
-/// The specified value of a CSS `<position>`
-pub type Position = GenericPosition<HorizontalPosition, VerticalPosition>;
-
-/// The specified value of an `auto | <position>`.
-pub type PositionOrAuto = GenericPositionOrAuto<Position>;
-
-/// The specified value of a horizontal position.
-pub type HorizontalPosition = PositionComponent<HorizontalPositionKeyword>;
-
-/// The specified value of a vertical position.
-pub type VerticalPosition = PositionComponent<VerticalPositionKeyword>;
-
-/// The specified value of a component of a CSS `<position>`.
-#[derive(Clone, Debug, MallocSizeOf, PartialEq, SpecifiedValueInfo, ToCss, ToShmem)]
-pub enum PositionComponent<S> {
- /// `center`
- Center,
- /// `<length-percentage>`
- Length(LengthPercentage),
- /// `<side> <length-percentage>?`
- Side(S, Option<LengthPercentage>),
-}
-
-/// A keyword for the X direction.
-#[derive(
- Clone,
- Copy,
- Debug,
- Eq,
- Hash,
- MallocSizeOf,
- Parse,
- PartialEq,
- SpecifiedValueInfo,
- ToComputedValue,
- ToCss,
- ToResolvedValue,
- ToShmem,
-)]
-#[allow(missing_docs)]
-#[repr(u8)]
-pub enum HorizontalPositionKeyword {
- Left,
- Right,
-}
-
-/// A keyword for the Y direction.
-#[derive(
- Clone,
- Copy,
- Debug,
- Eq,
- Hash,
- MallocSizeOf,
- Parse,
- PartialEq,
- SpecifiedValueInfo,
- ToComputedValue,
- ToCss,
- ToResolvedValue,
- ToShmem,
-)]
-#[allow(missing_docs)]
-#[repr(u8)]
-pub enum VerticalPositionKeyword {
- Top,
- Bottom,
-}
-
-impl Parse for Position {
- fn parse<'i, 't>(
- context: &ParserContext,
- input: &mut Parser<'i, 't>,
- ) -> Result<Self, ParseError<'i>> {
- let position = Self::parse_three_value_quirky(context, input, AllowQuirks::No)?;
- if position.is_three_value_syntax() {
- return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError));
- }
- Ok(position)
- }
-}
-
-impl Position {
- /// Parses a `<bg-position>`, with quirks.
- pub fn parse_three_value_quirky<'i, 't>(
- context: &ParserContext,
- input: &mut Parser<'i, 't>,
- allow_quirks: AllowQuirks,
- ) -> Result<Self, ParseError<'i>> {
- match input.try_parse(|i| PositionComponent::parse_quirky(context, i, allow_quirks)) {
- Ok(x_pos @ PositionComponent::Center) => {
- if let Ok(y_pos) =
- input.try_parse(|i| PositionComponent::parse_quirky(context, i, allow_quirks))
- {
- return Ok(Self::new(x_pos, y_pos));
- }
- let x_pos = input
- .try_parse(|i| PositionComponent::parse_quirky(context, i, allow_quirks))
- .unwrap_or(x_pos);
- let y_pos = PositionComponent::Center;
- return Ok(Self::new(x_pos, y_pos));
- },
- Ok(PositionComponent::Side(x_keyword, lp)) => {
- if input
- .try_parse(|i| i.expect_ident_matching("center"))
- .is_ok()
- {
- let x_pos = PositionComponent::Side(x_keyword, lp);
- let y_pos = PositionComponent::Center;
- return Ok(Self::new(x_pos, y_pos));
- }
- if let Ok(y_keyword) = input.try_parse(VerticalPositionKeyword::parse) {
- let y_lp = input
- .try_parse(|i| LengthPercentage::parse_quirky(context, i, allow_quirks))
- .ok();
- let x_pos = PositionComponent::Side(x_keyword, lp);
- let y_pos = PositionComponent::Side(y_keyword, y_lp);
- return Ok(Self::new(x_pos, y_pos));
- }
- let x_pos = PositionComponent::Side(x_keyword, None);
- let y_pos = lp.map_or(PositionComponent::Center, PositionComponent::Length);
- return Ok(Self::new(x_pos, y_pos));
- },
- Ok(x_pos @ PositionComponent::Length(_)) => {
- if let Ok(y_keyword) = input.try_parse(VerticalPositionKeyword::parse) {
- let y_pos = PositionComponent::Side(y_keyword, None);
- return Ok(Self::new(x_pos, y_pos));
- }
- if let Ok(y_lp) =
- input.try_parse(|i| LengthPercentage::parse_quirky(context, i, allow_quirks))
- {
- let y_pos = PositionComponent::Length(y_lp);
- return Ok(Self::new(x_pos, y_pos));
- }
- let y_pos = PositionComponent::Center;
- let _ = input.try_parse(|i| i.expect_ident_matching("center"));
- return Ok(Self::new(x_pos, y_pos));
- },
- Err(_) => {},
- }
- let y_keyword = VerticalPositionKeyword::parse(input)?;
- let lp_and_x_pos: Result<_, ParseError> = input.try_parse(|i| {
- let y_lp = i
- .try_parse(|i| LengthPercentage::parse_quirky(context, i, allow_quirks))
- .ok();
- if let Ok(x_keyword) = i.try_parse(HorizontalPositionKeyword::parse) {
- let x_lp = i
- .try_parse(|i| LengthPercentage::parse_quirky(context, i, allow_quirks))
- .ok();
- let x_pos = PositionComponent::Side(x_keyword, x_lp);
- return Ok((y_lp, x_pos));
- };
- i.expect_ident_matching("center")?;
- let x_pos = PositionComponent::Center;
- Ok((y_lp, x_pos))
- });
- if let Ok((y_lp, x_pos)) = lp_and_x_pos {
- let y_pos = PositionComponent::Side(y_keyword, y_lp);
- return Ok(Self::new(x_pos, y_pos));
- }
- let x_pos = PositionComponent::Center;
- let y_pos = PositionComponent::Side(y_keyword, None);
- Ok(Self::new(x_pos, y_pos))
- }
-
- /// `center center`
- #[inline]
- pub fn center() -> Self {
- Self::new(PositionComponent::Center, PositionComponent::Center)
- }
-
- /// Returns true if this uses a 3 value syntax.
- #[inline]
- fn is_three_value_syntax(&self) -> bool {
- self.horizontal.component_count() != self.vertical.component_count()
- }
-}
-
-impl ToCss for Position {
- fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
- where
- W: Write,
- {
- match (&self.horizontal, &self.vertical) {
- (
- x_pos @ &PositionComponent::Side(_, Some(_)),
- &PositionComponent::Length(ref y_lp),
- ) => {
- x_pos.to_css(dest)?;
- dest.write_str(" top ")?;
- y_lp.to_css(dest)
- },
- (
- &PositionComponent::Length(ref x_lp),
- y_pos @ &PositionComponent::Side(_, Some(_)),
- ) => {
- dest.write_str("left ")?;
- x_lp.to_css(dest)?;
- dest.write_char(' ')?;
- y_pos.to_css(dest)
- },
- (x_pos, y_pos) => {
- x_pos.to_css(dest)?;
- dest.write_char(' ')?;
- y_pos.to_css(dest)
- },
- }
- }
-}
-
-impl<S: Parse> Parse for PositionComponent<S> {
- fn parse<'i, 't>(
- context: &ParserContext,
- input: &mut Parser<'i, 't>,
- ) -> Result<Self, ParseError<'i>> {
- Self::parse_quirky(context, input, AllowQuirks::No)
- }
-}
-
-impl<S: Parse> PositionComponent<S> {
- /// Parses a component of a CSS position, with quirks.
- pub fn parse_quirky<'i, 't>(
- context: &ParserContext,
- input: &mut Parser<'i, 't>,
- allow_quirks: AllowQuirks,
- ) -> Result<Self, ParseError<'i>> {
- if input
- .try_parse(|i| i.expect_ident_matching("center"))
- .is_ok()
- {
- return Ok(PositionComponent::Center);
- }
- if let Ok(lp) =
- input.try_parse(|i| LengthPercentage::parse_quirky(context, i, allow_quirks))
- {
- return Ok(PositionComponent::Length(lp));
- }
- let keyword = S::parse(context, input)?;
- let lp = input
- .try_parse(|i| LengthPercentage::parse_quirky(context, i, allow_quirks))
- .ok();
- Ok(PositionComponent::Side(keyword, lp))
- }
-}
-
-impl<S> GenericPositionComponent for PositionComponent<S> {
- fn is_center(&self) -> bool {
- match *self {
- PositionComponent::Center => true,
- PositionComponent::Length(LengthPercentage::Percentage(ref per)) => per.0 == 0.5,
- // 50% from any side is still the center.
- PositionComponent::Side(_, Some(LengthPercentage::Percentage(ref per))) => per.0 == 0.5,
- _ => false,
- }
- }
-}
-
-impl<S> PositionComponent<S> {
- /// `0%`
- pub fn zero() -> Self {
- PositionComponent::Length(LengthPercentage::Percentage(Percentage::zero()))
- }
-
- /// Returns the count of this component.
- fn component_count(&self) -> usize {
- match *self {
- PositionComponent::Length(..) | PositionComponent::Center => 1,
- PositionComponent::Side(_, ref lp) => {
- if lp.is_some() {
- 2
- } else {
- 1
- }
- },
- }
- }
-}
-
-impl<S: Side> ToComputedValue for PositionComponent<S> {
- type ComputedValue = ComputedLengthPercentage;
-
- fn to_computed_value(&self, context: &Context) -> Self::ComputedValue {
- match *self {
- PositionComponent::Center => ComputedLengthPercentage::new_percent(Percentage(0.5)),
- PositionComponent::Side(ref keyword, None) => {
- let p = Percentage(if keyword.is_start() { 0. } else { 1. });
- ComputedLengthPercentage::new_percent(p)
- },
- PositionComponent::Side(ref keyword, Some(ref length)) if !keyword.is_start() => {
- let length = length.to_computed_value(context);
- // We represent `<end-side> <length>` as `calc(100% - <length>)`.
- ComputedLengthPercentage::hundred_percent_minus(length, AllowedNumericType::All)
- },
- PositionComponent::Side(_, Some(ref length)) |
- PositionComponent::Length(ref length) => length.to_computed_value(context),
- }
- }
-
- fn from_computed_value(computed: &Self::ComputedValue) -> Self {
- PositionComponent::Length(ToComputedValue::from_computed_value(computed))
- }
-}
-
-impl<S: Side> PositionComponent<S> {
- /// The initial specified value of a position component, i.e. the start side.
- pub fn initial_specified_value() -> Self {
- PositionComponent::Side(S::start(), None)
- }
-}
-
-/// Represents a side, either horizontal or vertical, of a CSS position.
-pub trait Side {
- /// Returns the start side.
- fn start() -> Self;
-
- /// Returns whether this side is the start side.
- fn is_start(&self) -> bool;
-}
-
-impl Side for HorizontalPositionKeyword {
- #[inline]
- fn start() -> Self {
- HorizontalPositionKeyword::Left
- }
-
- #[inline]
- fn is_start(&self) -> bool {
- *self == Self::start()
- }
-}
-
-impl Side for VerticalPositionKeyword {
- #[inline]
- fn start() -> Self {
- VerticalPositionKeyword::Top
- }
-
- #[inline]
- fn is_start(&self) -> bool {
- *self == Self::start()
- }
-}
-
-bitflags! {
- /// Controls how the auto-placement algorithm works specifying exactly how auto-placed items
- /// get flowed into the grid.
- #[derive(MallocSizeOf, SpecifiedValueInfo, ToComputedValue, ToResolvedValue, ToShmem)]
- #[value_info(other_values = "row,column,dense")]
- #[repr(C)]
- pub struct GridAutoFlow: u8 {
- /// 'row' - mutually exclusive with 'column'
- const ROW = 1 << 0;
- /// 'column' - mutually exclusive with 'row'
- const COLUMN = 1 << 1;
- /// 'dense'
- const DENSE = 1 << 2;
- }
-}
-
-#[repr(u8)]
-#[derive(
- Clone,
- Copy,
- Debug,
- Eq,
- MallocSizeOf,
- PartialEq,
- SpecifiedValueInfo,
- ToComputedValue,
- ToCss,
- ToResolvedValue,
- ToShmem,
-)]
-/// Masonry auto-placement algorithm packing.
-pub enum MasonryPlacement {
- /// Place the item in the track(s) with the smallest extent so far.
- Pack,
- /// Place the item after the last item, from start to end.
- Next,
-}
-
-#[repr(u8)]
-#[derive(
- Clone,
- Copy,
- Debug,
- Eq,
- MallocSizeOf,
- PartialEq,
- SpecifiedValueInfo,
- ToComputedValue,
- ToCss,
- ToResolvedValue,
- ToShmem,
-)]
-/// Masonry auto-placement algorithm item sorting option.
-pub enum MasonryItemOrder {
- /// Place all items with a definite placement before auto-placed items.
- DefiniteFirst,
- /// Place items in `order-modified document order`.
- Ordered,
-}
-
-#[derive(
- Clone,
- Copy,
- Debug,
- Eq,
- MallocSizeOf,
- PartialEq,
- SpecifiedValueInfo,
- ToComputedValue,
- ToCss,
- ToResolvedValue,
- ToShmem,
-)]
-#[repr(C)]
-/// Controls how the Masonry layout algorithm works
-/// specifying exactly how auto-placed items get flowed in the masonry axis.
-pub struct MasonryAutoFlow {
- /// Specify how to pick a auto-placement track.
- #[css(contextual_skip_if = "is_pack_with_non_default_order")]
- pub placement: MasonryPlacement,
- /// Specify how to pick an item to place.
- #[css(skip_if = "is_item_order_definite_first")]
- pub order: MasonryItemOrder,
-}
-
-#[inline]
-fn is_pack_with_non_default_order(placement: &MasonryPlacement, order: &MasonryItemOrder) -> bool {
- *placement == MasonryPlacement::Pack && *order != MasonryItemOrder::DefiniteFirst
-}
-
-#[inline]
-fn is_item_order_definite_first(order: &MasonryItemOrder) -> bool {
- *order == MasonryItemOrder::DefiniteFirst
-}
-
-impl MasonryAutoFlow {
- #[inline]
- /// Get initial `masonry-auto-flow` value.
- pub fn initial() -> MasonryAutoFlow {
- MasonryAutoFlow {
- placement: MasonryPlacement::Pack,
- order: MasonryItemOrder::DefiniteFirst,
- }
- }
-}
-
-impl Parse for MasonryAutoFlow {
- /// [ definite-first | ordered ] || [ pack | next ]
- fn parse<'i, 't>(
- _context: &ParserContext,
- input: &mut Parser<'i, 't>,
- ) -> Result<MasonryAutoFlow, ParseError<'i>> {
- let mut value = MasonryAutoFlow::initial();
- let mut got_placement = false;
- let mut got_order = false;
- while !input.is_exhausted() {
- let location = input.current_source_location();
- let ident = input.expect_ident()?;
- let success = match_ignore_ascii_case! { &ident,
- "pack" if !got_placement => {
- got_placement = true;
- true
- },
- "next" if !got_placement => {
- value.placement = MasonryPlacement::Next;
- got_placement = true;
- true
- },
- "definite-first" if !got_order => {
- got_order = true;
- true
- },
- "ordered" if !got_order => {
- value.order = MasonryItemOrder::Ordered;
- got_order = true;
- true
- },
- _ => false
- };
- if !success {
- return Err(location
- .new_custom_error(SelectorParseErrorKind::UnexpectedIdent(ident.clone())));
- }
- }
-
- if got_placement || got_order {
- Ok(value)
- } else {
- Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError))
- }
- }
-}
-
-// TODO: Can be derived with some care.
-impl Parse for GridAutoFlow {
- /// [ row | column ] || dense
- fn parse<'i, 't>(
- _context: &ParserContext,
- input: &mut Parser<'i, 't>,
- ) -> Result<GridAutoFlow, ParseError<'i>> {
- let mut track = None;
- let mut dense = GridAutoFlow::empty();
-
- while !input.is_exhausted() {
- let location = input.current_source_location();
- let ident = input.expect_ident()?;
- let success = match_ignore_ascii_case! { &ident,
- "row" if track.is_none() => {
- track = Some(GridAutoFlow::ROW);
- true
- },
- "column" if track.is_none() => {
- track = Some(GridAutoFlow::COLUMN);
- true
- },
- "dense" if dense.is_empty() => {
- dense = GridAutoFlow::DENSE;
- true
- },
- _ => false,
- };
- if !success {
- return Err(location
- .new_custom_error(SelectorParseErrorKind::UnexpectedIdent(ident.clone())));
- }
- }
-
- if track.is_some() || !dense.is_empty() {
- Ok(track.unwrap_or(GridAutoFlow::ROW) | dense)
- } else {
- Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError))
- }
- }
-}
-
-impl ToCss for GridAutoFlow {
- fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
- where
- W: Write,
- {
- if *self == GridAutoFlow::ROW {
- return dest.write_str("row");
- }
-
- if *self == GridAutoFlow::COLUMN {
- return dest.write_str("column");
- }
-
- if *self == GridAutoFlow::ROW | GridAutoFlow::DENSE {
- return dest.write_str("dense");
- }
-
- if *self == GridAutoFlow::COLUMN | GridAutoFlow::DENSE {
- return dest.write_str("column dense");
- }
-
- debug_assert!(false, "Unknown or invalid grid-autoflow value");
- Ok(())
- }
-}
-
-#[derive(
- Clone,
- Debug,
- MallocSizeOf,
- PartialEq,
- SpecifiedValueInfo,
- ToComputedValue,
- ToCss,
- ToResolvedValue,
- ToShmem,
-)]
-#[repr(C)]
-/// https://drafts.csswg.org/css-grid/#named-grid-area
-pub struct TemplateAreas {
- /// `named area` containing for each template area
- #[css(skip)]
- pub areas: crate::OwnedSlice<NamedArea>,
- /// The original CSS string value of each template area
- #[css(iterable)]
- pub strings: crate::OwnedSlice<crate::OwnedStr>,
- /// The number of columns of the grid.
- #[css(skip)]
- pub width: u32,
-}
-
-impl TemplateAreas {
- /// Transform `vector` of str into `template area`
- pub fn from_vec(strings: Vec<crate::OwnedStr>) -> Result<Self, ()> {
- if strings.is_empty() {
- return Err(());
- }
- let mut areas: Vec<NamedArea> = vec![];
- let mut width = 0;
- {
- let mut row = 0u32;
- let mut area_indices = PrecomputedHashMap::<Atom, usize>::default();
- for string in &strings {
- let mut current_area_index: Option<usize> = None;
- row += 1;
- let mut column = 0u32;
- for token in TemplateAreasTokenizer(string) {
- column += 1;
- let name = if let Some(token) = token? {
- Atom::from(token)
- } else {
- if let Some(index) = current_area_index.take() {
- if areas[index].columns.end != column {
- return Err(());
- }
- }
- continue;
- };
- if let Some(index) = current_area_index {
- if areas[index].name == name {
- if areas[index].rows.start == row {
- areas[index].columns.end += 1;
- }
- continue;
- }
- if areas[index].columns.end != column {
- return Err(());
- }
- }
- if let Some(index) = area_indices.get(&name).cloned() {
- if areas[index].columns.start != column || areas[index].rows.end != row {
- return Err(());
- }
- areas[index].rows.end += 1;
- current_area_index = Some(index);
- continue;
- }
- let index = areas.len();
- assert!(area_indices.insert(name.clone(), index).is_none());
- areas.push(NamedArea {
- name,
- columns: UnsignedRange {
- start: column,
- end: column + 1,
- },
- rows: UnsignedRange {
- start: row,
- end: row + 1,
- },
- });
- current_area_index = Some(index);
- }
- if column == 0 {
- // Each string must produce a valid token.
- // https://github.com/w3c/csswg-drafts/issues/5110
- return Err(());
- }
- if let Some(index) = current_area_index {
- if areas[index].columns.end != column + 1 {
- assert_ne!(areas[index].rows.start, row);
- return Err(());
- }
- }
- if row == 1 {
- width = column;
- } else if width != column {
- return Err(());
- }
- }
- }
- Ok(TemplateAreas {
- areas: areas.into(),
- strings: strings.into(),
- width,
- })
- }
-}
-
-impl Parse for TemplateAreas {
- fn parse<'i, 't>(
- _context: &ParserContext,
- input: &mut Parser<'i, 't>,
- ) -> Result<Self, ParseError<'i>> {
- let mut strings = vec![];
- while let Ok(string) =
- input.try_parse(|i| i.expect_string().map(|s| s.as_ref().to_owned().into()))
- {
- strings.push(string);
- }
-
- TemplateAreas::from_vec(strings)
- .map_err(|()| input.new_custom_error(StyleParseErrorKind::UnspecifiedError))
- }
-}
-
-/// Arc type for `Arc<TemplateAreas>`
-#[derive(
- Clone,
- Debug,
- MallocSizeOf,
- PartialEq,
- SpecifiedValueInfo,
- ToComputedValue,
- ToCss,
- ToResolvedValue,
- ToShmem,
-)]
-#[repr(transparent)]
-pub struct TemplateAreasArc(#[ignore_malloc_size_of = "Arc"] pub Arc<TemplateAreas>);
-
-impl Parse for TemplateAreasArc {
- fn parse<'i, 't>(
- context: &ParserContext,
- input: &mut Parser<'i, 't>,
- ) -> Result<Self, ParseError<'i>> {
- let parsed = TemplateAreas::parse(context, input)?;
- Ok(TemplateAreasArc(Arc::new(parsed)))
- }
-}
-
-/// A range of rows or columns. Using this instead of std::ops::Range for FFI
-/// purposes.
-#[repr(C)]
-#[derive(
- Clone,
- Debug,
- MallocSizeOf,
- PartialEq,
- SpecifiedValueInfo,
- ToComputedValue,
- ToResolvedValue,
- ToShmem,
-)]
-pub struct UnsignedRange {
- /// The start of the range.
- pub start: u32,
- /// The end of the range.
- pub end: u32,
-}
-
-#[derive(
- Clone,
- Debug,
- MallocSizeOf,
- PartialEq,
- SpecifiedValueInfo,
- ToComputedValue,
- ToResolvedValue,
- ToShmem,
-)]
-#[repr(C)]
-/// Not associated with any particular grid item, but can be referenced from the
-/// grid-placement properties.
-pub struct NamedArea {
- /// Name of the `named area`
- pub name: Atom,
- /// Rows of the `named area`
- pub rows: UnsignedRange,
- /// Columns of the `named area`
- pub columns: UnsignedRange,
-}
-
-/// Tokenize the string into a list of the tokens,
-/// using longest-match semantics
-struct TemplateAreasTokenizer<'a>(&'a str);
-
-impl<'a> Iterator for TemplateAreasTokenizer<'a> {
- type Item = Result<Option<&'a str>, ()>;
-
- fn next(&mut self) -> Option<Self::Item> {
- let rest = self.0.trim_start_matches(HTML_SPACE_CHARACTERS);
- if rest.is_empty() {
- return None;
- }
- if rest.starts_with('.') {
- self.0 = &rest[rest.find(|c| c != '.').unwrap_or(rest.len())..];
- return Some(Ok(None));
- }
- if !rest.starts_with(is_name_code_point) {
- return Some(Err(()));
- }
- let token_len = rest.find(|c| !is_name_code_point(c)).unwrap_or(rest.len());
- let token = &rest[..token_len];
- self.0 = &rest[token_len..];
- Some(Ok(Some(token)))
- }
-}
-
-fn is_name_code_point(c: char) -> bool {
- c >= 'A' && c <= 'Z' ||
- c >= 'a' && c <= 'z' ||
- c >= '\u{80}' ||
- c == '_' ||
- c >= '0' && c <= '9' ||
- c == '-'
-}
-
-/// This property specifies named grid areas.
-///
-/// The syntax of this property also provides a visualization of the structure
-/// of the grid, making the overall layout of the grid container easier to
-/// understand.
-#[repr(C, u8)]
-#[derive(
- Clone,
- Debug,
- MallocSizeOf,
- Parse,
- PartialEq,
- SpecifiedValueInfo,
- ToComputedValue,
- ToCss,
- ToResolvedValue,
- ToShmem,
-)]
-pub enum GridTemplateAreas {
- /// The `none` value.
- None,
- /// The actual value.
- Areas(TemplateAreasArc),
-}
-
-impl GridTemplateAreas {
- #[inline]
- /// Get default value as `none`
- pub fn none() -> GridTemplateAreas {
- GridTemplateAreas::None
- }
-}
-
-/// A specified value for the `z-index` property.
-pub type ZIndex = GenericZIndex<Integer>;
-
-/// A specified value for the `aspect-ratio` property.
-pub type AspectRatio = GenericAspectRatio<NonNegativeNumber>;
-
-impl Parse for AspectRatio {
- fn parse<'i, 't>(
- context: &ParserContext,
- input: &mut Parser<'i, 't>,
- ) -> Result<Self, ParseError<'i>> {
- use crate::values::generics::position::PreferredRatio;
- use crate::values::specified::Ratio;
-
- let location = input.current_source_location();
- let mut auto = input.try_parse(|i| i.expect_ident_matching("auto"));
- let ratio = input.try_parse(|i| Ratio::parse(context, i));
- if auto.is_err() {
- auto = input.try_parse(|i| i.expect_ident_matching("auto"));
- }
-
- if auto.is_err() && ratio.is_err() {
- return Err(location.new_custom_error(StyleParseErrorKind::UnspecifiedError));
- }
-
- Ok(AspectRatio {
- auto: auto.is_ok(),
- ratio: match ratio {
- Ok(ratio) => PreferredRatio::Ratio(ratio),
- Err(..) => PreferredRatio::None,
- },
- })
- }
-}
-
-impl AspectRatio {
- /// Returns Self by a valid ratio.
- pub fn from_mapped_ratio(w: f32, h: f32) -> Self {
- use crate::values::generics::position::PreferredRatio;
- use crate::values::generics::ratio::Ratio;
- AspectRatio {
- auto: true,
- ratio: PreferredRatio::Ratio(Ratio(
- NonNegativeNumber::new(w),
- NonNegativeNumber::new(h),
- )),
- }
- }
-}
diff --git a/components/style/values/specified/ratio.rs b/components/style/values/specified/ratio.rs
deleted file mode 100644
index 4cdddd452ed..00000000000
--- a/components/style/values/specified/ratio.rs
+++ /dev/null
@@ -1,32 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
-
-//! Specified types for <ratio>.
-//!
-//! [ratio]: https://drafts.csswg.org/css-values/#ratios
-
-use crate::parser::{Parse, ParserContext};
-use crate::values::generics::ratio::Ratio as GenericRatio;
-use crate::values::specified::NonNegativeNumber;
-use crate::One;
-use cssparser::Parser;
-use style_traits::ParseError;
-
-/// A specified <ratio> value.
-pub type Ratio = GenericRatio<NonNegativeNumber>;
-
-impl Parse for Ratio {
- fn parse<'i, 't>(
- context: &ParserContext,
- input: &mut Parser<'i, 't>,
- ) -> Result<Self, ParseError<'i>> {
- let a = NonNegativeNumber::parse(context, input)?;
- let b = match input.try_parse(|input| input.expect_delim('/')) {
- Ok(()) => NonNegativeNumber::parse(context, input)?,
- _ => One::one(),
- };
-
- Ok(GenericRatio(a, b))
- }
-}
diff --git a/components/style/values/specified/rect.rs b/components/style/values/specified/rect.rs
deleted file mode 100644
index 7955ecaa48d..00000000000
--- a/components/style/values/specified/rect.rs
+++ /dev/null
@@ -1,11 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
-
-//! Specified types for CSS borders.
-
-use crate::values::generics::rect::Rect;
-use crate::values::specified::length::NonNegativeLengthOrNumber;
-
-/// A specified rectangle made of four `<length-or-number>` values.
-pub type NonNegativeLengthOrNumberRect = Rect<NonNegativeLengthOrNumber>;
diff --git a/components/style/values/specified/resolution.rs b/components/style/values/specified/resolution.rs
deleted file mode 100644
index 74f100972a7..00000000000
--- a/components/style/values/specified/resolution.rs
+++ /dev/null
@@ -1,141 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
-
-//! Resolution values:
-//!
-//! https://drafts.csswg.org/css-values/#resolution
-
-use crate::parser::{Parse, ParserContext};
-use crate::values::specified::CalcNode;
-use crate::values::CSSFloat;
-use cssparser::{Parser, Token};
-use std::fmt::{self, Write};
-use style_traits::{CssWriter, ParseError, StyleParseErrorKind, ToCss};
-
-/// A specified resolution.
-#[derive(Clone, Debug, MallocSizeOf, PartialEq, SpecifiedValueInfo, ToShmem)]
-pub struct Resolution {
- value: CSSFloat,
- unit: ResolutionUnit,
- was_calc: bool,
-}
-
-#[derive(Clone, Copy, Debug, MallocSizeOf, PartialEq, SpecifiedValueInfo, ToCss, ToShmem)]
-enum ResolutionUnit {
- /// Dots per inch.
- Dpi,
- /// An alias unit for dots per pixel.
- X,
- /// Dots per pixel.
- Dppx,
- /// Dots per centimeter.
- Dpcm,
-}
-
-impl ResolutionUnit {
- fn as_str(self) -> &'static str {
- match self {
- Self::Dpi => "dpi",
- Self::X => "x",
- Self::Dppx => "dppx",
- Self::Dpcm => "dpcm",
- }
- }
-}
-
-impl Resolution {
- /// Returns a resolution value from dppx units.
- pub fn from_dppx(value: CSSFloat) -> Self {
- Self {
- value,
- unit: ResolutionUnit::Dppx,
- was_calc: false,
- }
- }
-
- /// Returns a resolution value from dppx units.
- pub fn from_x(value: CSSFloat) -> Self {
- Self {
- value,
- unit: ResolutionUnit::X,
- was_calc: false,
- }
- }
-
- /// Returns a resolution value from dppx units.
- pub fn from_dppx_calc(value: CSSFloat) -> Self {
- Self {
- value,
- unit: ResolutionUnit::Dppx,
- was_calc: true,
- }
- }
-
- /// Convert this resolution value to dppx units.
- pub fn dppx(&self) -> CSSFloat {
- match self.unit {
- ResolutionUnit::X | ResolutionUnit::Dppx => self.value,
- _ => self.dpi() / 96.0,
- }
- }
-
- /// Convert this resolution value to dpi units.
- pub fn dpi(&self) -> CSSFloat {
- match self.unit {
- ResolutionUnit::Dpi => self.value,
- ResolutionUnit::X | ResolutionUnit::Dppx => self.value * 96.0,
- ResolutionUnit::Dpcm => self.value * 2.54,
- }
- }
-
- /// Parse a resolution given a value and unit.
- pub fn parse_dimension<'i, 't>(value: CSSFloat, unit: &str) -> Result<Self, ()> {
- let unit = match_ignore_ascii_case! { &unit,
- "dpi" => ResolutionUnit::Dpi,
- "dppx" => ResolutionUnit::Dppx,
- "dpcm" => ResolutionUnit::Dpcm,
- "x" => ResolutionUnit::X,
- _ => return Err(())
- };
- Ok(Self {
- value,
- unit,
- was_calc: false,
- })
- }
-}
-
-impl ToCss for Resolution {
- fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
- where
- W: Write,
- {
- crate::values::serialize_specified_dimension(
- self.value,
- self.unit.as_str(),
- self.was_calc,
- dest,
- )
- }
-}
-
-impl Parse for Resolution {
- fn parse<'i, 't>(
- context: &ParserContext,
- input: &mut Parser<'i, 't>,
- ) -> Result<Self, ParseError<'i>> {
- let location = input.current_source_location();
- match *input.next()? {
- Token::Dimension {
- value, ref unit, ..
- } if value >= 0. => Self::parse_dimension(value, unit)
- .map_err(|()| location.new_custom_error(StyleParseErrorKind::UnspecifiedError)),
- Token::Function(ref name) => {
- let function = CalcNode::math_function(context, name, location)?;
- CalcNode::parse_resolution(context, input, function)
- },
- ref t => return Err(location.new_unexpected_token_error(t.clone())),
- }
- }
-}
diff --git a/components/style/values/specified/source_size_list.rs b/components/style/values/specified/source_size_list.rs
deleted file mode 100644
index ac47461cc48..00000000000
--- a/components/style/values/specified/source_size_list.rs
+++ /dev/null
@@ -1,136 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
-
-//! https://html.spec.whatwg.org/multipage/#source-size-list
-
-use crate::media_queries::Device;
-use crate::parser::{Parse, ParserContext};
-use crate::queries::{FeatureType, QueryCondition};
-use crate::values::computed::{self, ToComputedValue};
-use crate::values::specified::{Length, NoCalcLength, ViewportPercentageLength};
-use app_units::Au;
-use cssparser::{Delimiter, Parser, Token};
-use selectors::context::QuirksMode;
-use style_traits::ParseError;
-
-/// A value for a `<source-size>`:
-///
-/// https://html.spec.whatwg.org/multipage/#source-size
-#[derive(Debug)]
-pub struct SourceSize {
- condition: QueryCondition,
- value: Length,
-}
-
-impl Parse for SourceSize {
- fn parse<'i, 't>(
- context: &ParserContext,
- input: &mut Parser<'i, 't>,
- ) -> Result<Self, ParseError<'i>> {
- let condition = QueryCondition::parse(context, input, FeatureType::Media)?;
- let value = Length::parse_non_negative(context, input)?;
- Ok(Self { condition, value })
- }
-}
-
-/// A value for a `<source-size-list>`:
-///
-/// https://html.spec.whatwg.org/multipage/#source-size-list
-#[derive(Debug)]
-pub struct SourceSizeList {
- source_sizes: Vec<SourceSize>,
- value: Option<Length>,
-}
-
-impl SourceSizeList {
- /// Create an empty `SourceSizeList`, which can be used as a fall-back.
- pub fn empty() -> Self {
- Self {
- source_sizes: vec![],
- value: None,
- }
- }
-
- /// Evaluate this <source-size-list> to get the final viewport length.
- pub fn evaluate(&self, device: &Device, quirks_mode: QuirksMode) -> Au {
- computed::Context::for_media_query_evaluation(device, quirks_mode, |context| {
- let matching_source_size = self.source_sizes.iter().find(|source_size| {
- source_size
- .condition
- .matches(context)
- .to_bool(/* unknown = */ false)
- });
-
- match matching_source_size {
- Some(source_size) => source_size.value.to_computed_value(context),
- None => match self.value {
- Some(ref v) => v.to_computed_value(context),
- None => Length::NoCalc(NoCalcLength::ViewportPercentage(
- ViewportPercentageLength::Vw(100.),
- ))
- .to_computed_value(context),
- },
- }
- })
- .into()
- }
-}
-
-enum SourceSizeOrLength {
- SourceSize(SourceSize),
- Length(Length),
-}
-
-impl Parse for SourceSizeOrLength {
- fn parse<'i, 't>(
- context: &ParserContext,
- input: &mut Parser<'i, 't>,
- ) -> Result<Self, ParseError<'i>> {
- if let Ok(size) = input.try_parse(|input| SourceSize::parse(context, input)) {
- return Ok(SourceSizeOrLength::SourceSize(size));
- }
-
- let length = Length::parse_non_negative(context, input)?;
- Ok(SourceSizeOrLength::Length(length))
- }
-}
-
-impl SourceSizeList {
- /// NOTE(emilio): This doesn't match the grammar in the spec, see:
- ///
- /// https://html.spec.whatwg.org/multipage/#parsing-a-sizes-attribute
- pub fn parse<'i, 't>(context: &ParserContext, input: &mut Parser<'i, 't>) -> Self {
- let mut source_sizes = vec![];
-
- loop {
- let result = input.parse_until_before(Delimiter::Comma, |input| {
- SourceSizeOrLength::parse(context, input)
- });
-
- match result {
- Ok(SourceSizeOrLength::Length(value)) => {
- return Self {
- source_sizes,
- value: Some(value),
- };
- },
- Ok(SourceSizeOrLength::SourceSize(source_size)) => {
- source_sizes.push(source_size);
- },
- Err(..) => {},
- }
-
- match input.next() {
- Ok(&Token::Comma) => {},
- Err(..) => break,
- _ => unreachable!(),
- }
- }
-
- SourceSizeList {
- source_sizes,
- value: None,
- }
- }
-}
diff --git a/components/style/values/specified/svg.rs b/components/style/values/specified/svg.rs
deleted file mode 100644
index 24b7d3f3aec..00000000000
--- a/components/style/values/specified/svg.rs
+++ /dev/null
@@ -1,391 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
-
-//! Specified types for SVG properties.
-
-use crate::parser::{Parse, ParserContext};
-use crate::values::generics::svg as generic;
-use crate::values::specified::color::Color;
-use crate::values::specified::url::SpecifiedUrl;
-use crate::values::specified::AllowQuirks;
-use crate::values::specified::LengthPercentage;
-use crate::values::specified::SVGPathData;
-use crate::values::specified::{NonNegativeLengthPercentage, Opacity};
-use crate::values::CustomIdent;
-use cssparser::{Parser, Token};
-use std::fmt::{self, Write};
-use style_traits::{CommaWithSpace, CssWriter, ParseError, Separator};
-use style_traits::{StyleParseErrorKind, ToCss};
-
-/// Specified SVG Paint value
-pub type SVGPaint = generic::GenericSVGPaint<Color, SpecifiedUrl>;
-
-/// <length> | <percentage> | <number> | context-value
-pub type SVGLength = generic::GenericSVGLength<LengthPercentage>;
-
-/// A non-negative version of SVGLength.
-pub type SVGWidth = generic::GenericSVGLength<NonNegativeLengthPercentage>;
-
-/// [ <length> | <percentage> | <number> ]# | context-value
-pub type SVGStrokeDashArray = generic::GenericSVGStrokeDashArray<NonNegativeLengthPercentage>;
-
-/// Whether the `context-value` value is enabled.
-#[cfg(feature = "gecko")]
-pub fn is_context_value_enabled() -> bool {
- static_prefs::pref!("gfx.font_rendering.opentype_svg.enabled")
-}
-
-/// Whether the `context-value` value is enabled.
-#[cfg(not(feature = "gecko"))]
-pub fn is_context_value_enabled() -> bool {
- false
-}
-
-macro_rules! parse_svg_length {
- ($ty:ty, $lp:ty) => {
- impl Parse for $ty {
- fn parse<'i, 't>(
- context: &ParserContext,
- input: &mut Parser<'i, 't>,
- ) -> Result<Self, ParseError<'i>> {
- if let Ok(lp) =
- input.try_parse(|i| <$lp>::parse_quirky(context, i, AllowQuirks::Always))
- {
- return Ok(generic::SVGLength::LengthPercentage(lp));
- }
-
- try_match_ident_ignore_ascii_case! { input,
- "context-value" if is_context_value_enabled() => {
- Ok(generic::SVGLength::ContextValue)
- },
- }
- }
- }
- };
-}
-
-parse_svg_length!(SVGLength, LengthPercentage);
-parse_svg_length!(SVGWidth, NonNegativeLengthPercentage);
-
-impl Parse for SVGStrokeDashArray {
- fn parse<'i, 't>(
- context: &ParserContext,
- input: &mut Parser<'i, 't>,
- ) -> Result<Self, ParseError<'i>> {
- if let Ok(values) = input.try_parse(|i| {
- CommaWithSpace::parse(i, |i| {
- NonNegativeLengthPercentage::parse_quirky(context, i, AllowQuirks::Always)
- })
- }) {
- return Ok(generic::SVGStrokeDashArray::Values(values.into()));
- }
-
- try_match_ident_ignore_ascii_case! { input,
- "context-value" if is_context_value_enabled() => {
- Ok(generic::SVGStrokeDashArray::ContextValue)
- },
- "none" => Ok(generic::SVGStrokeDashArray::Values(Default::default())),
- }
- }
-}
-
-/// <opacity-value> | context-fill-opacity | context-stroke-opacity
-pub type SVGOpacity = generic::SVGOpacity<Opacity>;
-
-/// The specified value for a single CSS paint-order property.
-#[repr(u8)]
-#[derive(Clone, Copy, Debug, Eq, Ord, PartialEq, PartialOrd, ToCss)]
-pub enum PaintOrder {
- /// `normal` variant
- Normal = 0,
- /// `fill` variant
- Fill = 1,
- /// `stroke` variant
- Stroke = 2,
- /// `markers` variant
- Markers = 3,
-}
-
-/// Number of non-normal components
-pub const PAINT_ORDER_COUNT: u8 = 3;
-
-/// Number of bits for each component
-pub const PAINT_ORDER_SHIFT: u8 = 2;
-
-/// Mask with above bits set
-pub const PAINT_ORDER_MASK: u8 = 0b11;
-
-/// The specified value is tree `PaintOrder` values packed into the
-/// bitfields below, as a six-bit field, of 3 two-bit pairs
-///
-/// Each pair can be set to FILL, STROKE, or MARKERS
-/// Lowest significant bit pairs are highest priority.
-/// `normal` is the empty bitfield. The three pairs are
-/// never zero in any case other than `normal`.
-///
-/// Higher priority values, i.e. the values specified first,
-/// will be painted first (and may be covered by paintings of lower priority)
-#[derive(
- Clone,
- Copy,
- Debug,
- MallocSizeOf,
- PartialEq,
- SpecifiedValueInfo,
- ToComputedValue,
- ToResolvedValue,
- ToShmem,
-)]
-#[repr(transparent)]
-pub struct SVGPaintOrder(pub u8);
-
-impl SVGPaintOrder {
- /// Get default `paint-order` with `0`
- pub fn normal() -> Self {
- SVGPaintOrder(0)
- }
-
- /// Get variant of `paint-order`
- pub fn order_at(&self, pos: u8) -> PaintOrder {
- match (self.0 >> pos * PAINT_ORDER_SHIFT) & PAINT_ORDER_MASK {
- 0 => PaintOrder::Normal,
- 1 => PaintOrder::Fill,
- 2 => PaintOrder::Stroke,
- 3 => PaintOrder::Markers,
- _ => unreachable!("this cannot happen"),
- }
- }
-}
-
-impl Parse for SVGPaintOrder {
- fn parse<'i, 't>(
- _context: &ParserContext,
- input: &mut Parser<'i, 't>,
- ) -> Result<SVGPaintOrder, ParseError<'i>> {
- if let Ok(()) = input.try_parse(|i| i.expect_ident_matching("normal")) {
- return Ok(SVGPaintOrder::normal());
- }
-
- let mut value = 0;
- // bitfield representing what we've seen so far
- // bit 1 is fill, bit 2 is stroke, bit 3 is markers
- let mut seen = 0;
- let mut pos = 0;
-
- loop {
- let result: Result<_, ParseError> = input.try_parse(|input| {
- try_match_ident_ignore_ascii_case! { input,
- "fill" => Ok(PaintOrder::Fill),
- "stroke" => Ok(PaintOrder::Stroke),
- "markers" => Ok(PaintOrder::Markers),
- }
- });
-
- match result {
- Ok(val) => {
- if (seen & (1 << val as u8)) != 0 {
- // don't parse the same ident twice
- return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError));
- }
-
- value |= (val as u8) << (pos * PAINT_ORDER_SHIFT);
- seen |= 1 << (val as u8);
- pos += 1;
- },
- Err(_) => break,
- }
- }
-
- if value == 0 {
- // Couldn't find any keyword
- return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError));
- }
-
- // fill in rest
- for i in pos..PAINT_ORDER_COUNT {
- for paint in 1..(PAINT_ORDER_COUNT + 1) {
- // if not seen, set bit at position, mark as seen
- if (seen & (1 << paint)) == 0 {
- seen |= 1 << paint;
- value |= paint << (i * PAINT_ORDER_SHIFT);
- break;
- }
- }
- }
-
- Ok(SVGPaintOrder(value))
- }
-}
-
-impl ToCss for SVGPaintOrder {
- fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
- where
- W: Write,
- {
- if self.0 == 0 {
- return dest.write_str("normal");
- }
-
- let mut last_pos_to_serialize = 0;
- for i in (1..PAINT_ORDER_COUNT).rev() {
- let component = self.order_at(i);
- let earlier_component = self.order_at(i - 1);
- if component < earlier_component {
- last_pos_to_serialize = i - 1;
- break;
- }
- }
-
- for pos in 0..last_pos_to_serialize + 1 {
- if pos != 0 {
- dest.write_char(' ')?
- }
- self.order_at(pos).to_css(dest)?;
- }
- Ok(())
- }
-}
-
-bitflags! {
- /// The context properties we understand.
- #[derive(Default, MallocSizeOf, SpecifiedValueInfo, ToComputedValue, ToResolvedValue, ToShmem)]
- #[repr(C)]
- pub struct ContextPropertyBits: u8 {
- /// `fill`
- const FILL = 1 << 0;
- /// `stroke`
- const STROKE = 1 << 1;
- /// `fill-opacity`
- const FILL_OPACITY = 1 << 2;
- /// `stroke-opacity`
- const STROKE_OPACITY = 1 << 3;
- }
-}
-
-/// Specified MozContextProperties value.
-/// Nonstandard (https://developer.mozilla.org/en-US/docs/Web/CSS/-moz-context-properties)
-#[derive(
- Clone,
- Debug,
- Default,
- MallocSizeOf,
- PartialEq,
- SpecifiedValueInfo,
- ToComputedValue,
- ToCss,
- ToResolvedValue,
- ToShmem,
-)]
-#[repr(C)]
-pub struct MozContextProperties {
- #[css(iterable, if_empty = "none")]
- #[ignore_malloc_size_of = "Arc"]
- idents: crate::ArcSlice<CustomIdent>,
- #[css(skip)]
- bits: ContextPropertyBits,
-}
-
-impl Parse for MozContextProperties {
- fn parse<'i, 't>(
- _context: &ParserContext,
- input: &mut Parser<'i, 't>,
- ) -> Result<MozContextProperties, ParseError<'i>> {
- let mut values = vec![];
- let mut bits = ContextPropertyBits::empty();
- loop {
- {
- let location = input.current_source_location();
- let ident = input.expect_ident()?;
-
- if ident.eq_ignore_ascii_case("none") && values.is_empty() {
- return Ok(Self::default());
- }
-
- let ident = CustomIdent::from_ident(location, ident, &["all", "none", "auto"])?;
-
- if ident.0 == atom!("fill") {
- bits.insert(ContextPropertyBits::FILL);
- } else if ident.0 == atom!("stroke") {
- bits.insert(ContextPropertyBits::STROKE);
- } else if ident.0 == atom!("fill-opacity") {
- bits.insert(ContextPropertyBits::FILL_OPACITY);
- } else if ident.0 == atom!("stroke-opacity") {
- bits.insert(ContextPropertyBits::STROKE_OPACITY);
- }
-
- values.push(ident);
- }
-
- let location = input.current_source_location();
- match input.next() {
- Ok(&Token::Comma) => continue,
- Err(..) => break,
- Ok(other) => return Err(location.new_unexpected_token_error(other.clone())),
- }
- }
-
- if values.is_empty() {
- return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError));
- }
-
- Ok(MozContextProperties {
- idents: crate::ArcSlice::from_iter(values.into_iter()),
- bits,
- })
- }
-}
-
-/// The svg d property type.
-///
-/// https://svgwg.org/svg2-draft/paths.html#TheDProperty
-#[derive(
- Animate,
- Clone,
- ComputeSquaredDistance,
- Debug,
- Deserialize,
- MallocSizeOf,
- PartialEq,
- Serialize,
- SpecifiedValueInfo,
- ToAnimatedZero,
- ToComputedValue,
- ToCss,
- ToResolvedValue,
- ToShmem,
-)]
-#[repr(C, u8)]
-pub enum DProperty {
- /// Path value for path(<string>) or just a <string>.
- #[css(function)]
- Path(SVGPathData),
- /// None value.
- #[animation(error)]
- None,
-}
-
-impl DProperty {
- /// return none.
- #[inline]
- pub fn none() -> Self {
- DProperty::None
- }
-}
-
-impl Parse for DProperty {
- fn parse<'i, 't>(
- context: &ParserContext,
- input: &mut Parser<'i, 't>,
- ) -> Result<Self, ParseError<'i>> {
- // Parse none.
- if input.try_parse(|i| i.expect_ident_matching("none")).is_ok() {
- return Ok(DProperty::none());
- }
-
- // Parse possible functions.
- input.expect_function_matching("path")?;
- let path_data = input.parse_nested_block(|i| Parse::parse(context, i))?;
- Ok(DProperty::Path(path_data))
- }
-}
diff --git a/components/style/values/specified/svg_path.rs b/components/style/values/specified/svg_path.rs
deleted file mode 100644
index 3b33a188fec..00000000000
--- a/components/style/values/specified/svg_path.rs
+++ /dev/null
@@ -1,1030 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
-
-//! Specified types for SVG Path.
-
-use crate::parser::{Parse, ParserContext};
-use crate::values::animated::{lists, Animate, Procedure, ToAnimatedZero};
-use crate::values::distance::{ComputeSquaredDistance, SquaredDistance};
-use crate::values::CSSFloat;
-use cssparser::Parser;
-use std::fmt::{self, Write};
-use std::iter::{Cloned, Peekable};
-use std::slice;
-use style_traits::values::SequenceWriter;
-use style_traits::{CssWriter, ParseError, StyleParseErrorKind, ToCss};
-
-/// Whether to allow empty string in the parser.
-#[derive(Clone, Debug, Eq, PartialEq)]
-#[allow(missing_docs)]
-pub enum AllowEmpty {
- Yes,
- No,
-}
-
-/// The SVG path data.
-///
-/// https://www.w3.org/TR/SVG11/paths.html#PathData
-#[derive(
- Clone,
- Debug,
- Deserialize,
- MallocSizeOf,
- PartialEq,
- Serialize,
- SpecifiedValueInfo,
- ToAnimatedZero,
- ToComputedValue,
- ToResolvedValue,
- ToShmem,
-)]
-#[repr(C)]
-pub struct SVGPathData(
- // TODO(emilio): Should probably measure this somehow only from the
- // specified values.
- #[ignore_malloc_size_of = "Arc"] pub crate::ArcSlice<PathCommand>,
-);
-
-impl SVGPathData {
- /// Get the array of PathCommand.
- #[inline]
- pub fn commands(&self) -> &[PathCommand] {
- &self.0
- }
-
- /// Create a normalized copy of this path by converting each relative
- /// command to an absolute command.
- pub fn normalize(&self) -> Self {
- let mut state = PathTraversalState {
- subpath_start: CoordPair::new(0.0, 0.0),
- pos: CoordPair::new(0.0, 0.0),
- };
- let iter = self.0.iter().map(|seg| seg.normalize(&mut state));
- SVGPathData(crate::ArcSlice::from_iter(iter))
- }
-
- // FIXME: Bug 1714238, we may drop this once we use the same data structure for both SVG and
- // CSS.
- /// Decode the svg path raw data from Gecko.
- #[cfg(feature = "gecko")]
- pub fn decode_from_f32_array(path: &[f32]) -> Result<Self, ()> {
- use crate::gecko_bindings::structs::dom::SVGPathSeg_Binding::*;
-
- let mut result: Vec<PathCommand> = Vec::new();
- let mut i: usize = 0;
- while i < path.len() {
- // See EncodeType() and DecodeType() in SVGPathSegUtils.h.
- // We are using reinterpret_cast<> to encode and decode between u32 and f32, so here we
- // use to_bits() to decode the type.
- let seg_type = path[i].to_bits() as u16;
- i = i + 1;
- match seg_type {
- PATHSEG_CLOSEPATH => result.push(PathCommand::ClosePath),
- PATHSEG_MOVETO_ABS | PATHSEG_MOVETO_REL => {
- debug_assert!(i + 1 < path.len());
- result.push(PathCommand::MoveTo {
- point: CoordPair::new(path[i], path[i + 1]),
- absolute: IsAbsolute::new(seg_type == PATHSEG_MOVETO_ABS),
- });
- i = i + 2;
- },
- PATHSEG_LINETO_ABS | PATHSEG_LINETO_REL => {
- debug_assert!(i + 1 < path.len());
- result.push(PathCommand::LineTo {
- point: CoordPair::new(path[i], path[i + 1]),
- absolute: IsAbsolute::new(seg_type == PATHSEG_LINETO_ABS),
- });
- i = i + 2;
- },
- PATHSEG_CURVETO_CUBIC_ABS | PATHSEG_CURVETO_CUBIC_REL => {
- debug_assert!(i + 5 < path.len());
- result.push(PathCommand::CurveTo {
- control1: CoordPair::new(path[i], path[i + 1]),
- control2: CoordPair::new(path[i + 2], path[i + 3]),
- point: CoordPair::new(path[i + 4], path[i + 5]),
- absolute: IsAbsolute::new(seg_type == PATHSEG_CURVETO_CUBIC_ABS),
- });
- i = i + 6;
- },
- PATHSEG_CURVETO_QUADRATIC_ABS | PATHSEG_CURVETO_QUADRATIC_REL => {
- debug_assert!(i + 3 < path.len());
- result.push(PathCommand::QuadBezierCurveTo {
- control1: CoordPair::new(path[i], path[i + 1]),
- point: CoordPair::new(path[i + 2], path[i + 3]),
- absolute: IsAbsolute::new(seg_type == PATHSEG_CURVETO_QUADRATIC_ABS),
- });
- i = i + 4;
- },
- PATHSEG_ARC_ABS | PATHSEG_ARC_REL => {
- debug_assert!(i + 6 < path.len());
- result.push(PathCommand::EllipticalArc {
- rx: path[i],
- ry: path[i + 1],
- angle: path[i + 2],
- large_arc_flag: ArcFlag(path[i + 3] != 0.0f32),
- sweep_flag: ArcFlag(path[i + 4] != 0.0f32),
- point: CoordPair::new(path[i + 5], path[i + 6]),
- absolute: IsAbsolute::new(seg_type == PATHSEG_ARC_ABS),
- });
- i = i + 7;
- },
- PATHSEG_LINETO_HORIZONTAL_ABS | PATHSEG_LINETO_HORIZONTAL_REL => {
- debug_assert!(i < path.len());
- result.push(PathCommand::HorizontalLineTo {
- x: path[i],
- absolute: IsAbsolute::new(seg_type == PATHSEG_LINETO_HORIZONTAL_ABS),
- });
- i = i + 1;
- },
- PATHSEG_LINETO_VERTICAL_ABS | PATHSEG_LINETO_VERTICAL_REL => {
- debug_assert!(i < path.len());
- result.push(PathCommand::VerticalLineTo {
- y: path[i],
- absolute: IsAbsolute::new(seg_type == PATHSEG_LINETO_VERTICAL_ABS),
- });
- i = i + 1;
- },
- PATHSEG_CURVETO_CUBIC_SMOOTH_ABS | PATHSEG_CURVETO_CUBIC_SMOOTH_REL => {
- debug_assert!(i + 3 < path.len());
- result.push(PathCommand::SmoothCurveTo {
- control2: CoordPair::new(path[i], path[i + 1]),
- point: CoordPair::new(path[i + 2], path[i + 3]),
- absolute: IsAbsolute::new(seg_type == PATHSEG_CURVETO_CUBIC_SMOOTH_ABS),
- });
- i = i + 4;
- },
- PATHSEG_CURVETO_QUADRATIC_SMOOTH_ABS | PATHSEG_CURVETO_QUADRATIC_SMOOTH_REL => {
- debug_assert!(i + 1 < path.len());
- result.push(PathCommand::SmoothQuadBezierCurveTo {
- point: CoordPair::new(path[i], path[i + 1]),
- absolute: IsAbsolute::new(seg_type == PATHSEG_CURVETO_QUADRATIC_SMOOTH_ABS),
- });
- i = i + 2;
- },
- PATHSEG_UNKNOWN | _ => return Err(()),
- }
- }
-
- Ok(SVGPathData(crate::ArcSlice::from_iter(result.into_iter())))
- }
-
- /// Parse this SVG path string with the argument that indicates whether we should allow the
- /// empty string.
- // We cannot use cssparser::Parser to parse a SVG path string because the spec wants to make
- // the SVG path string as compact as possible. (i.e. The whitespaces may be dropped.)
- // e.g. "M100 200L100 200" is a valid SVG path string. If we use tokenizer, the first ident
- // is "M100", instead of "M", and this is not correct. Therefore, we use a Peekable
- // str::Char iterator to check each character.
- pub fn parse<'i, 't>(
- input: &mut Parser<'i, 't>,
- allow_empty: AllowEmpty,
- ) -> Result<Self, ParseError<'i>> {
- let location = input.current_source_location();
- let path_string = input.expect_string()?.as_ref();
-
- // Parse the svg path string as multiple sub-paths.
- let mut path_parser = PathParser::new(path_string);
- while skip_wsp(&mut path_parser.chars) {
- if path_parser.parse_subpath().is_err() {
- return Err(location.new_custom_error(StyleParseErrorKind::UnspecifiedError));
- }
- }
-
- // The css-shapes-1 says a path data string that does conform but defines an empty path is
- // invalid and causes the entire path() to be invalid, so we use the argement to decide
- // whether we should allow the empty string.
- // https://drafts.csswg.org/css-shapes-1/#typedef-basic-shape
- if matches!(allow_empty, AllowEmpty::No) && path_parser.path.is_empty() {
- return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError));
- }
-
- Ok(SVGPathData(crate::ArcSlice::from_iter(
- path_parser.path.into_iter(),
- )))
- }
-}
-
-impl ToCss for SVGPathData {
- #[inline]
- fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
- where
- W: fmt::Write,
- {
- dest.write_char('"')?;
- {
- let mut writer = SequenceWriter::new(dest, " ");
- for command in self.commands() {
- writer.item(command)?;
- }
- }
- dest.write_char('"')
- }
-}
-
-impl Parse for SVGPathData {
- fn parse<'i, 't>(
- _context: &ParserContext,
- input: &mut Parser<'i, 't>,
- ) -> Result<Self, ParseError<'i>> {
- // Note that the EBNF allows the path data string in the d property to be empty, so we
- // don't reject empty SVG path data.
- // https://svgwg.org/svg2-draft/single-page.html#paths-PathDataBNF
- SVGPathData::parse(input, AllowEmpty::Yes)
- }
-}
-
-impl Animate for SVGPathData {
- fn animate(&self, other: &Self, procedure: Procedure) -> Result<Self, ()> {
- if self.0.len() != other.0.len() {
- return Err(());
- }
-
- // FIXME(emilio): This allocates three copies of the path, that's not
- // great! Specially, once we're normalized once, we don't need to
- // re-normalize again.
- let left = self.normalize();
- let right = other.normalize();
-
- let items: Vec<_> = lists::by_computed_value::animate(&left.0, &right.0, procedure)?;
- Ok(SVGPathData(crate::ArcSlice::from_iter(items.into_iter())))
- }
-}
-
-impl ComputeSquaredDistance for SVGPathData {
- fn compute_squared_distance(&self, other: &Self) -> Result<SquaredDistance, ()> {
- if self.0.len() != other.0.len() {
- return Err(());
- }
- let left = self.normalize();
- let right = other.normalize();
- lists::by_computed_value::squared_distance(&left.0, &right.0)
- }
-}
-
-/// The SVG path command.
-/// The fields of these commands are self-explanatory, so we skip the documents.
-/// Note: the index of the control points, e.g. control1, control2, are mapping to the control
-/// points of the Bézier curve in the spec.
-///
-/// https://www.w3.org/TR/SVG11/paths.html#PathData
-#[derive(
- Animate,
- Clone,
- ComputeSquaredDistance,
- Copy,
- Debug,
- Deserialize,
- MallocSizeOf,
- PartialEq,
- Serialize,
- SpecifiedValueInfo,
- ToAnimatedZero,
- ToComputedValue,
- ToResolvedValue,
- ToShmem,
-)]
-#[allow(missing_docs)]
-#[repr(C, u8)]
-pub enum PathCommand {
- /// The unknown type.
- /// https://www.w3.org/TR/SVG/paths.html#__svg__SVGPathSeg__PATHSEG_UNKNOWN
- Unknown,
- /// The "moveto" command.
- MoveTo {
- point: CoordPair,
- absolute: IsAbsolute,
- },
- /// The "lineto" command.
- LineTo {
- point: CoordPair,
- absolute: IsAbsolute,
- },
- /// The horizontal "lineto" command.
- HorizontalLineTo { x: CSSFloat, absolute: IsAbsolute },
- /// The vertical "lineto" command.
- VerticalLineTo { y: CSSFloat, absolute: IsAbsolute },
- /// The cubic Bézier curve command.
- CurveTo {
- control1: CoordPair,
- control2: CoordPair,
- point: CoordPair,
- absolute: IsAbsolute,
- },
- /// The smooth curve command.
- SmoothCurveTo {
- control2: CoordPair,
- point: CoordPair,
- absolute: IsAbsolute,
- },
- /// The quadratic Bézier curve command.
- QuadBezierCurveTo {
- control1: CoordPair,
- point: CoordPair,
- absolute: IsAbsolute,
- },
- /// The smooth quadratic Bézier curve command.
- SmoothQuadBezierCurveTo {
- point: CoordPair,
- absolute: IsAbsolute,
- },
- /// The elliptical arc curve command.
- EllipticalArc {
- rx: CSSFloat,
- ry: CSSFloat,
- angle: CSSFloat,
- large_arc_flag: ArcFlag,
- sweep_flag: ArcFlag,
- point: CoordPair,
- absolute: IsAbsolute,
- },
- /// The "closepath" command.
- ClosePath,
-}
-
-/// For internal SVGPath normalization.
-#[allow(missing_docs)]
-struct PathTraversalState {
- subpath_start: CoordPair,
- pos: CoordPair,
-}
-
-impl PathCommand {
- /// Create a normalized copy of this PathCommand. Absolute commands will be copied as-is while
- /// for relative commands an equivalent absolute command will be returned.
- ///
- /// See discussion: https://github.com/w3c/svgwg/issues/321
- fn normalize(&self, state: &mut PathTraversalState) -> Self {
- use self::PathCommand::*;
- match *self {
- Unknown => Unknown,
- ClosePath => {
- state.pos = state.subpath_start;
- ClosePath
- },
- MoveTo {
- mut point,
- absolute,
- } => {
- if !absolute.is_yes() {
- point += state.pos;
- }
- state.pos = point;
- state.subpath_start = point;
- MoveTo {
- point,
- absolute: IsAbsolute::Yes,
- }
- },
- LineTo {
- mut point,
- absolute,
- } => {
- if !absolute.is_yes() {
- point += state.pos;
- }
- state.pos = point;
- LineTo {
- point,
- absolute: IsAbsolute::Yes,
- }
- },
- HorizontalLineTo { mut x, absolute } => {
- if !absolute.is_yes() {
- x += state.pos.x;
- }
- state.pos.x = x;
- HorizontalLineTo {
- x,
- absolute: IsAbsolute::Yes,
- }
- },
- VerticalLineTo { mut y, absolute } => {
- if !absolute.is_yes() {
- y += state.pos.y;
- }
- state.pos.y = y;
- VerticalLineTo {
- y,
- absolute: IsAbsolute::Yes,
- }
- },
- CurveTo {
- mut control1,
- mut control2,
- mut point,
- absolute,
- } => {
- if !absolute.is_yes() {
- control1 += state.pos;
- control2 += state.pos;
- point += state.pos;
- }
- state.pos = point;
- CurveTo {
- control1,
- control2,
- point,
- absolute: IsAbsolute::Yes,
- }
- },
- SmoothCurveTo {
- mut control2,
- mut point,
- absolute,
- } => {
- if !absolute.is_yes() {
- control2 += state.pos;
- point += state.pos;
- }
- state.pos = point;
- SmoothCurveTo {
- control2,
- point,
- absolute: IsAbsolute::Yes,
- }
- },
- QuadBezierCurveTo {
- mut control1,
- mut point,
- absolute,
- } => {
- if !absolute.is_yes() {
- control1 += state.pos;
- point += state.pos;
- }
- state.pos = point;
- QuadBezierCurveTo {
- control1,
- point,
- absolute: IsAbsolute::Yes,
- }
- },
- SmoothQuadBezierCurveTo {
- mut point,
- absolute,
- } => {
- if !absolute.is_yes() {
- point += state.pos;
- }
- state.pos = point;
- SmoothQuadBezierCurveTo {
- point,
- absolute: IsAbsolute::Yes,
- }
- },
- EllipticalArc {
- rx,
- ry,
- angle,
- large_arc_flag,
- sweep_flag,
- mut point,
- absolute,
- } => {
- if !absolute.is_yes() {
- point += state.pos;
- }
- state.pos = point;
- EllipticalArc {
- rx,
- ry,
- angle,
- large_arc_flag,
- sweep_flag,
- point,
- absolute: IsAbsolute::Yes,
- }
- },
- }
- }
-}
-
-impl ToCss for PathCommand {
- fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
- where
- W: fmt::Write,
- {
- use self::PathCommand::*;
- match *self {
- Unknown => dest.write_char('X'),
- ClosePath => dest.write_char('Z'),
- MoveTo { point, absolute } => {
- dest.write_char(if absolute.is_yes() { 'M' } else { 'm' })?;
- dest.write_char(' ')?;
- point.to_css(dest)
- },
- LineTo { point, absolute } => {
- dest.write_char(if absolute.is_yes() { 'L' } else { 'l' })?;
- dest.write_char(' ')?;
- point.to_css(dest)
- },
- CurveTo {
- control1,
- control2,
- point,
- absolute,
- } => {
- dest.write_char(if absolute.is_yes() { 'C' } else { 'c' })?;
- dest.write_char(' ')?;
- control1.to_css(dest)?;
- dest.write_char(' ')?;
- control2.to_css(dest)?;
- dest.write_char(' ')?;
- point.to_css(dest)
- },
- QuadBezierCurveTo {
- control1,
- point,
- absolute,
- } => {
- dest.write_char(if absolute.is_yes() { 'Q' } else { 'q' })?;
- dest.write_char(' ')?;
- control1.to_css(dest)?;
- dest.write_char(' ')?;
- point.to_css(dest)
- },
- EllipticalArc {
- rx,
- ry,
- angle,
- large_arc_flag,
- sweep_flag,
- point,
- absolute,
- } => {
- dest.write_char(if absolute.is_yes() { 'A' } else { 'a' })?;
- dest.write_char(' ')?;
- rx.to_css(dest)?;
- dest.write_char(' ')?;
- ry.to_css(dest)?;
- dest.write_char(' ')?;
- angle.to_css(dest)?;
- dest.write_char(' ')?;
- large_arc_flag.to_css(dest)?;
- dest.write_char(' ')?;
- sweep_flag.to_css(dest)?;
- dest.write_char(' ')?;
- point.to_css(dest)
- },
- HorizontalLineTo { x, absolute } => {
- dest.write_char(if absolute.is_yes() { 'H' } else { 'h' })?;
- dest.write_char(' ')?;
- x.to_css(dest)
- },
- VerticalLineTo { y, absolute } => {
- dest.write_char(if absolute.is_yes() { 'V' } else { 'v' })?;
- dest.write_char(' ')?;
- y.to_css(dest)
- },
- SmoothCurveTo {
- control2,
- point,
- absolute,
- } => {
- dest.write_char(if absolute.is_yes() { 'S' } else { 's' })?;
- dest.write_char(' ')?;
- control2.to_css(dest)?;
- dest.write_char(' ')?;
- point.to_css(dest)
- },
- SmoothQuadBezierCurveTo { point, absolute } => {
- dest.write_char(if absolute.is_yes() { 'T' } else { 't' })?;
- dest.write_char(' ')?;
- point.to_css(dest)
- },
- }
- }
-}
-
-/// The path command absolute type.
-#[allow(missing_docs)]
-#[derive(
- Animate,
- Clone,
- ComputeSquaredDistance,
- Copy,
- Debug,
- Deserialize,
- MallocSizeOf,
- PartialEq,
- Serialize,
- SpecifiedValueInfo,
- ToAnimatedZero,
- ToComputedValue,
- ToResolvedValue,
- ToShmem,
-)]
-#[repr(u8)]
-pub enum IsAbsolute {
- Yes,
- No,
-}
-
-impl IsAbsolute {
- /// Return true if this is IsAbsolute::Yes.
- #[inline]
- pub fn is_yes(&self) -> bool {
- *self == IsAbsolute::Yes
- }
-
- /// Return Yes if value is true. Otherwise, return No.
- #[inline]
- #[cfg(feature = "gecko")]
- fn new(value: bool) -> Self {
- if value {
- IsAbsolute::Yes
- } else {
- IsAbsolute::No
- }
- }
-}
-
-/// The path coord type.
-#[allow(missing_docs)]
-#[derive(
- AddAssign,
- Animate,
- Clone,
- ComputeSquaredDistance,
- Copy,
- Debug,
- Deserialize,
- MallocSizeOf,
- PartialEq,
- Serialize,
- SpecifiedValueInfo,
- ToAnimatedZero,
- ToComputedValue,
- ToCss,
- ToResolvedValue,
- ToShmem,
-)]
-#[repr(C)]
-pub struct CoordPair {
- x: CSSFloat,
- y: CSSFloat,
-}
-
-impl CoordPair {
- /// Create a CoordPair.
- #[inline]
- pub fn new(x: CSSFloat, y: CSSFloat) -> Self {
- CoordPair { x, y }
- }
-}
-
-/// The EllipticalArc flag type.
-#[derive(
- Clone,
- Copy,
- Debug,
- Deserialize,
- MallocSizeOf,
- PartialEq,
- Serialize,
- SpecifiedValueInfo,
- ToComputedValue,
- ToResolvedValue,
- ToShmem,
-)]
-#[repr(C)]
-pub struct ArcFlag(bool);
-
-impl ToCss for ArcFlag {
- #[inline]
- fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
- where
- W: fmt::Write,
- {
- (self.0 as i32).to_css(dest)
- }
-}
-
-impl Animate for ArcFlag {
- #[inline]
- fn animate(&self, other: &Self, procedure: Procedure) -> Result<Self, ()> {
- (self.0 as i32)
- .animate(&(other.0 as i32), procedure)
- .map(|v| ArcFlag(v > 0))
- }
-}
-
-impl ComputeSquaredDistance for ArcFlag {
- #[inline]
- fn compute_squared_distance(&self, other: &Self) -> Result<SquaredDistance, ()> {
- (self.0 as i32).compute_squared_distance(&(other.0 as i32))
- }
-}
-
-impl ToAnimatedZero for ArcFlag {
- #[inline]
- fn to_animated_zero(&self) -> Result<Self, ()> {
- // The 2 ArcFlags in EllipticalArc determine which one of the 4 different arcs will be
- // used. (i.e. From 4 combinations). In other words, if we change the flag, we get a
- // different arc. Therefore, we return *self.
- // https://svgwg.org/svg2-draft/paths.html#PathDataEllipticalArcCommands
- Ok(*self)
- }
-}
-
-/// SVG Path parser.
-struct PathParser<'a> {
- chars: Peekable<Cloned<slice::Iter<'a, u8>>>,
- path: Vec<PathCommand>,
-}
-
-macro_rules! parse_arguments {
- (
- $parser:ident,
- $abs:ident,
- $enum:ident,
- [ $para:ident => $func:ident $(, $other_para:ident => $other_func:ident)* ]
- ) => {
- {
- loop {
- let $para = $func(&mut $parser.chars)?;
- $(
- skip_comma_wsp(&mut $parser.chars);
- let $other_para = $other_func(&mut $parser.chars)?;
- )*
- $parser.path.push(PathCommand::$enum { $para $(, $other_para)*, $abs });
-
- // End of string or the next character is a possible new command.
- if !skip_wsp(&mut $parser.chars) ||
- $parser.chars.peek().map_or(true, |c| c.is_ascii_alphabetic()) {
- break;
- }
- skip_comma_wsp(&mut $parser.chars);
- }
- Ok(())
- }
- }
-}
-
-impl<'a> PathParser<'a> {
- /// Return a PathParser.
- #[inline]
- fn new(string: &'a str) -> Self {
- PathParser {
- chars: string.as_bytes().iter().cloned().peekable(),
- path: Vec::new(),
- }
- }
-
- /// Parse a sub-path.
- fn parse_subpath(&mut self) -> Result<(), ()> {
- // Handle "moveto" Command first. If there is no "moveto", this is not a valid sub-path
- // (i.e. not a valid moveto-drawto-command-group).
- self.parse_moveto()?;
-
- // Handle other commands.
- loop {
- skip_wsp(&mut self.chars);
- if self.chars.peek().map_or(true, |&m| m == b'M' || m == b'm') {
- break;
- }
-
- let command = self.chars.next().unwrap();
- let abs = if command.is_ascii_uppercase() {
- IsAbsolute::Yes
- } else {
- IsAbsolute::No
- };
-
- skip_wsp(&mut self.chars);
- match command {
- b'Z' | b'z' => self.parse_closepath(),
- b'L' | b'l' => self.parse_lineto(abs),
- b'H' | b'h' => self.parse_h_lineto(abs),
- b'V' | b'v' => self.parse_v_lineto(abs),
- b'C' | b'c' => self.parse_curveto(abs),
- b'S' | b's' => self.parse_smooth_curveto(abs),
- b'Q' | b'q' => self.parse_quadratic_bezier_curveto(abs),
- b'T' | b't' => self.parse_smooth_quadratic_bezier_curveto(abs),
- b'A' | b'a' => self.parse_elliptical_arc(abs),
- _ => return Err(()),
- }?;
- }
- Ok(())
- }
-
- /// Parse "moveto" command.
- fn parse_moveto(&mut self) -> Result<(), ()> {
- let command = match self.chars.next() {
- Some(c) if c == b'M' || c == b'm' => c,
- _ => return Err(()),
- };
-
- skip_wsp(&mut self.chars);
- let point = parse_coord(&mut self.chars)?;
- let absolute = if command == b'M' {
- IsAbsolute::Yes
- } else {
- IsAbsolute::No
- };
- self.path.push(PathCommand::MoveTo { point, absolute });
-
- // End of string or the next character is a possible new command.
- if !skip_wsp(&mut self.chars) || self.chars.peek().map_or(true, |c| c.is_ascii_alphabetic())
- {
- return Ok(());
- }
- skip_comma_wsp(&mut self.chars);
-
- // If a moveto is followed by multiple pairs of coordinates, the subsequent
- // pairs are treated as implicit lineto commands.
- self.parse_lineto(absolute)
- }
-
- /// Parse "closepath" command.
- fn parse_closepath(&mut self) -> Result<(), ()> {
- self.path.push(PathCommand::ClosePath);
- Ok(())
- }
-
- /// Parse "lineto" command.
- fn parse_lineto(&mut self, absolute: IsAbsolute) -> Result<(), ()> {
- parse_arguments!(self, absolute, LineTo, [ point => parse_coord ])
- }
-
- /// Parse horizontal "lineto" command.
- fn parse_h_lineto(&mut self, absolute: IsAbsolute) -> Result<(), ()> {
- parse_arguments!(self, absolute, HorizontalLineTo, [ x => parse_number ])
- }
-
- /// Parse vertical "lineto" command.
- fn parse_v_lineto(&mut self, absolute: IsAbsolute) -> Result<(), ()> {
- parse_arguments!(self, absolute, VerticalLineTo, [ y => parse_number ])
- }
-
- /// Parse cubic Bézier curve command.
- fn parse_curveto(&mut self, absolute: IsAbsolute) -> Result<(), ()> {
- parse_arguments!(self, absolute, CurveTo, [
- control1 => parse_coord, control2 => parse_coord, point => parse_coord
- ])
- }
-
- /// Parse smooth "curveto" command.
- fn parse_smooth_curveto(&mut self, absolute: IsAbsolute) -> Result<(), ()> {
- parse_arguments!(self, absolute, SmoothCurveTo, [
- control2 => parse_coord, point => parse_coord
- ])
- }
-
- /// Parse quadratic Bézier curve command.
- fn parse_quadratic_bezier_curveto(&mut self, absolute: IsAbsolute) -> Result<(), ()> {
- parse_arguments!(self, absolute, QuadBezierCurveTo, [
- control1 => parse_coord, point => parse_coord
- ])
- }
-
- /// Parse smooth quadratic Bézier curveto command.
- fn parse_smooth_quadratic_bezier_curveto(&mut self, absolute: IsAbsolute) -> Result<(), ()> {
- parse_arguments!(self, absolute, SmoothQuadBezierCurveTo, [ point => parse_coord ])
- }
-
- /// Parse elliptical arc curve command.
- fn parse_elliptical_arc(&mut self, absolute: IsAbsolute) -> Result<(), ()> {
- // Parse a flag whose value is '0' or '1'; otherwise, return Err(()).
- let parse_flag = |iter: &mut Peekable<Cloned<slice::Iter<u8>>>| match iter.next() {
- Some(c) if c == b'0' || c == b'1' => Ok(ArcFlag(c == b'1')),
- _ => Err(()),
- };
- parse_arguments!(self, absolute, EllipticalArc, [
- rx => parse_number,
- ry => parse_number,
- angle => parse_number,
- large_arc_flag => parse_flag,
- sweep_flag => parse_flag,
- point => parse_coord
- ])
- }
-}
-
-/// Parse a pair of numbers into CoordPair.
-fn parse_coord(iter: &mut Peekable<Cloned<slice::Iter<u8>>>) -> Result<CoordPair, ()> {
- let x = parse_number(iter)?;
- skip_comma_wsp(iter);
- let y = parse_number(iter)?;
- Ok(CoordPair::new(x, y))
-}
-
-/// This is a special version which parses the number for SVG Path. e.g. "M 0.6.5" should be parsed
-/// as MoveTo with a coordinate of ("0.6", ".5"), instead of treating 0.6.5 as a non-valid floating
-/// point number. In other words, the logic here is similar with that of
-/// tokenizer::consume_numeric, which also consumes the number as many as possible, but here the
-/// input is a Peekable and we only accept an integer of a floating point number.
-///
-/// The "number" syntax in https://www.w3.org/TR/SVG/paths.html#PathDataBNF
-fn parse_number(iter: &mut Peekable<Cloned<slice::Iter<u8>>>) -> Result<CSSFloat, ()> {
- // 1. Check optional sign.
- let sign = if iter
- .peek()
- .map_or(false, |&sign| sign == b'+' || sign == b'-')
- {
- if iter.next().unwrap() == b'-' {
- -1.
- } else {
- 1.
- }
- } else {
- 1.
- };
-
- // 2. Check integer part.
- let mut integral_part: f64 = 0.;
- let got_dot = if !iter.peek().map_or(false, |&n| n == b'.') {
- // If the first digit in integer part is neither a dot nor a digit, this is not a number.
- if iter.peek().map_or(true, |n| !n.is_ascii_digit()) {
- return Err(());
- }
-
- while iter.peek().map_or(false, |n| n.is_ascii_digit()) {
- integral_part = integral_part * 10. + (iter.next().unwrap() - b'0') as f64;
- }
-
- iter.peek().map_or(false, |&n| n == b'.')
- } else {
- true
- };
-
- // 3. Check fractional part.
- let mut fractional_part: f64 = 0.;
- if got_dot {
- // Consume '.'.
- iter.next();
- // If the first digit in fractional part is not a digit, this is not a number.
- if iter.peek().map_or(true, |n| !n.is_ascii_digit()) {
- return Err(());
- }
-
- let mut factor = 0.1;
- while iter.peek().map_or(false, |n| n.is_ascii_digit()) {
- fractional_part += (iter.next().unwrap() - b'0') as f64 * factor;
- factor *= 0.1;
- }
- }
-
- let mut value = sign * (integral_part + fractional_part);
-
- // 4. Check exp part. The segment name of SVG Path doesn't include 'E' or 'e', so it's ok to
- // treat the numbers after 'E' or 'e' are in the exponential part.
- if iter.peek().map_or(false, |&exp| exp == b'E' || exp == b'e') {
- // Consume 'E' or 'e'.
- iter.next();
- let exp_sign = if iter
- .peek()
- .map_or(false, |&sign| sign == b'+' || sign == b'-')
- {
- if iter.next().unwrap() == b'-' {
- -1.
- } else {
- 1.
- }
- } else {
- 1.
- };
-
- let mut exp: f64 = 0.;
- while iter.peek().map_or(false, |n| n.is_ascii_digit()) {
- exp = exp * 10. + (iter.next().unwrap() - b'0') as f64;
- }
-
- value *= f64::powf(10., exp * exp_sign);
- }
-
- if value.is_finite() {
- Ok(value.min(f32::MAX as f64).max(f32::MIN as f64) as CSSFloat)
- } else {
- Err(())
- }
-}
-
-/// Skip all svg whitespaces, and return true if |iter| hasn't finished.
-#[inline]
-fn skip_wsp(iter: &mut Peekable<Cloned<slice::Iter<u8>>>) -> bool {
- // Note: SVG 1.1 defines the whitespaces as \u{9}, \u{20}, \u{A}, \u{D}.
- // However, SVG 2 has one extra whitespace: \u{C}.
- // Therefore, we follow the newest spec for the definition of whitespace,
- // i.e. \u{9}, \u{20}, \u{A}, \u{C}, \u{D}.
- while iter.peek().map_or(false, |c| c.is_ascii_whitespace()) {
- iter.next();
- }
- iter.peek().is_some()
-}
-
-/// Skip all svg whitespaces and one comma, and return true if |iter| hasn't finished.
-#[inline]
-fn skip_comma_wsp(iter: &mut Peekable<Cloned<slice::Iter<u8>>>) -> bool {
- if !skip_wsp(iter) {
- return false;
- }
-
- if *iter.peek().unwrap() != b',' {
- return true;
- }
- iter.next();
-
- skip_wsp(iter)
-}
diff --git a/components/style/values/specified/table.rs b/components/style/values/specified/table.rs
deleted file mode 100644
index 88f917ac78d..00000000000
--- a/components/style/values/specified/table.rs
+++ /dev/null
@@ -1,36 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
-
-//! Specified types for CSS values related to tables.
-
-/// Specified values for the `caption-side` property.
-///
-/// Note that despite having "physical" names, these are actually interpreted
-/// according to the table's writing-mode: Top and Bottom are treated as
-/// block-start and -end respectively.
-///
-/// https://drafts.csswg.org/css-tables/#propdef-caption-side
-#[allow(missing_docs)]
-#[derive(
- Clone,
- Copy,
- Debug,
- Eq,
- FromPrimitive,
- MallocSizeOf,
- Ord,
- Parse,
- PartialEq,
- PartialOrd,
- SpecifiedValueInfo,
- ToComputedValue,
- ToCss,
- ToResolvedValue,
- ToShmem,
-)]
-#[repr(u8)]
-pub enum CaptionSide {
- Top,
- Bottom,
-}
diff --git a/components/style/values/specified/text.rs b/components/style/values/specified/text.rs
deleted file mode 100644
index 8545a4fc01a..00000000000
--- a/components/style/values/specified/text.rs
+++ /dev/null
@@ -1,1154 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
-
-//! Specified types for text properties.
-
-use crate::parser::{Parse, ParserContext};
-use crate::properties::longhands::writing_mode::computed_value::T as SpecifiedWritingMode;
-use crate::values::computed::text::LineHeight as ComputedLineHeight;
-use crate::values::computed::text::TextEmphasisStyle as ComputedTextEmphasisStyle;
-use crate::values::computed::text::TextOverflow as ComputedTextOverflow;
-use crate::values::computed::{Context, ToComputedValue};
-use crate::values::generics::text::InitialLetter as GenericInitialLetter;
-use crate::values::generics::text::LineHeight as GenericLineHeight;
-use crate::values::generics::text::{GenericTextDecorationLength, Spacing};
-use crate::values::specified::length::NonNegativeLengthPercentage;
-use crate::values::specified::length::{FontRelativeLength, Length};
-use crate::values::specified::length::{LengthPercentage, NoCalcLength};
-use crate::values::specified::{AllowQuirks, Integer, NonNegativeNumber, Number};
-use cssparser::{Parser, Token};
-use selectors::parser::SelectorParseErrorKind;
-use std::fmt::{self, Write};
-use style_traits::values::SequenceWriter;
-use style_traits::{CssWriter, ParseError, StyleParseErrorKind, ToCss};
-use style_traits::{KeywordsCollectFn, SpecifiedValueInfo};
-use unicode_segmentation::UnicodeSegmentation;
-
-/// A specified type for the `initial-letter` property.
-pub type InitialLetter = GenericInitialLetter<Number, Integer>;
-
-/// A specified value for the `letter-spacing` property.
-pub type LetterSpacing = Spacing<Length>;
-
-/// A specified value for the `word-spacing` property.
-pub type WordSpacing = Spacing<LengthPercentage>;
-
-/// A specified value for the `line-height` property.
-pub type LineHeight = GenericLineHeight<NonNegativeNumber, NonNegativeLengthPercentage>;
-
-/// A value for the `hyphenate-character` property.
-#[derive(
- Clone,
- Debug,
- MallocSizeOf,
- Parse,
- PartialEq,
- SpecifiedValueInfo,
- ToComputedValue,
- ToCss,
- ToResolvedValue,
- ToShmem,
-)]
-#[repr(C, u8)]
-pub enum HyphenateCharacter {
- /// `auto`
- Auto,
- /// `<string>`
- String(crate::OwnedStr),
-}
-
-impl Parse for InitialLetter {
- fn parse<'i, 't>(
- context: &ParserContext,
- input: &mut Parser<'i, 't>,
- ) -> Result<Self, ParseError<'i>> {
- if input
- .try_parse(|i| i.expect_ident_matching("normal"))
- .is_ok()
- {
- return Ok(GenericInitialLetter::Normal);
- }
- let size = Number::parse_at_least_one(context, input)?;
- let sink = input
- .try_parse(|i| Integer::parse_positive(context, i))
- .ok();
- Ok(GenericInitialLetter::Specified(size, sink))
- }
-}
-
-impl Parse for LetterSpacing {
- fn parse<'i, 't>(
- context: &ParserContext,
- input: &mut Parser<'i, 't>,
- ) -> Result<Self, ParseError<'i>> {
- Spacing::parse_with(context, input, |c, i| {
- Length::parse_quirky(c, i, AllowQuirks::Yes)
- })
- }
-}
-
-impl Parse for WordSpacing {
- fn parse<'i, 't>(
- context: &ParserContext,
- input: &mut Parser<'i, 't>,
- ) -> Result<Self, ParseError<'i>> {
- Spacing::parse_with(context, input, |c, i| {
- LengthPercentage::parse_quirky(c, i, AllowQuirks::Yes)
- })
- }
-}
-
-impl ToComputedValue for LineHeight {
- type ComputedValue = ComputedLineHeight;
-
- #[inline]
- fn to_computed_value(&self, context: &Context) -> Self::ComputedValue {
- use crate::values::specified::length::FontBaseSize;
- match *self {
- GenericLineHeight::Normal => GenericLineHeight::Normal,
- #[cfg(feature = "gecko")]
- GenericLineHeight::MozBlockHeight => GenericLineHeight::MozBlockHeight,
- GenericLineHeight::Number(number) => {
- GenericLineHeight::Number(number.to_computed_value(context))
- },
- GenericLineHeight::Length(ref non_negative_lp) => {
- let result = match non_negative_lp.0 {
- LengthPercentage::Length(NoCalcLength::Absolute(ref abs)) => {
- context.maybe_zoom_text(abs.to_computed_value(context))
- },
- LengthPercentage::Length(ref length) => length.to_computed_value(context),
- LengthPercentage::Percentage(ref p) => FontRelativeLength::Em(p.0)
- .to_computed_value(context, FontBaseSize::CurrentStyle),
- LengthPercentage::Calc(ref calc) => {
- let computed_calc =
- calc.to_computed_value_zoomed(context, FontBaseSize::CurrentStyle);
- let base = context.style().get_font().clone_font_size().computed_size();
- computed_calc.resolve(base)
- },
- };
- GenericLineHeight::Length(result.into())
- },
- }
- }
-
- #[inline]
- fn from_computed_value(computed: &Self::ComputedValue) -> Self {
- match *computed {
- GenericLineHeight::Normal => GenericLineHeight::Normal,
- #[cfg(feature = "gecko")]
- GenericLineHeight::MozBlockHeight => GenericLineHeight::MozBlockHeight,
- GenericLineHeight::Number(ref number) => {
- GenericLineHeight::Number(NonNegativeNumber::from_computed_value(number))
- },
- GenericLineHeight::Length(ref length) => {
- GenericLineHeight::Length(NoCalcLength::from_computed_value(&length.0).into())
- },
- }
- }
-}
-
-/// A generic value for the `text-overflow` property.
-#[derive(
- Clone,
- Debug,
- Eq,
- MallocSizeOf,
- PartialEq,
- Parse,
- SpecifiedValueInfo,
- ToComputedValue,
- ToCss,
- ToResolvedValue,
- ToShmem,
-)]
-#[repr(C, u8)]
-pub enum TextOverflowSide {
- /// Clip inline content.
- Clip,
- /// Render ellipsis to represent clipped inline content.
- Ellipsis,
- /// Render a given string to represent clipped inline content.
- String(crate::OwnedStr),
-}
-
-#[derive(Clone, Debug, Eq, MallocSizeOf, PartialEq, SpecifiedValueInfo, ToCss, ToShmem)]
-/// text-overflow. Specifies rendering when inline content overflows its line box edge.
-pub struct TextOverflow {
- /// First value. Applies to end line box edge if no second is supplied; line-left edge otherwise.
- pub first: TextOverflowSide,
- /// Second value. Applies to the line-right edge if supplied.
- pub second: Option<TextOverflowSide>,
-}
-
-impl Parse for TextOverflow {
- fn parse<'i, 't>(
- context: &ParserContext,
- input: &mut Parser<'i, 't>,
- ) -> Result<TextOverflow, ParseError<'i>> {
- let first = TextOverflowSide::parse(context, input)?;
- let second = input
- .try_parse(|input| TextOverflowSide::parse(context, input))
- .ok();
- Ok(TextOverflow { first, second })
- }
-}
-
-impl ToComputedValue for TextOverflow {
- type ComputedValue = ComputedTextOverflow;
-
- #[inline]
- fn to_computed_value(&self, _context: &Context) -> Self::ComputedValue {
- if let Some(ref second) = self.second {
- Self::ComputedValue {
- first: self.first.clone(),
- second: second.clone(),
- sides_are_logical: false,
- }
- } else {
- Self::ComputedValue {
- first: TextOverflowSide::Clip,
- second: self.first.clone(),
- sides_are_logical: true,
- }
- }
- }
-
- #[inline]
- fn from_computed_value(computed: &Self::ComputedValue) -> Self {
- if computed.sides_are_logical {
- assert_eq!(computed.first, TextOverflowSide::Clip);
- TextOverflow {
- first: computed.second.clone(),
- second: None,
- }
- } else {
- TextOverflow {
- first: computed.first.clone(),
- second: Some(computed.second.clone()),
- }
- }
- }
-}
-
-bitflags! {
- #[derive(MallocSizeOf, Parse, Serialize, SpecifiedValueInfo, ToCss, ToComputedValue, ToResolvedValue, ToShmem)]
- #[css(bitflags(single = "none", mixed = "underline,overline,line-through,blink"))]
- #[repr(C)]
- /// Specified keyword values for the text-decoration-line property.
- pub struct TextDecorationLine: u8 {
- /// No text decoration line is specified.
- const NONE = 0;
- /// underline
- const UNDERLINE = 1 << 0;
- /// overline
- const OVERLINE = 1 << 1;
- /// line-through
- const LINE_THROUGH = 1 << 2;
- /// blink
- const BLINK = 1 << 3;
- /// Only set by presentation attributes
- ///
- /// Setting this will mean that text-decorations use the color
- /// specified by `color` in quirks mode.
- ///
- /// For example, this gives <a href=foo><font color="red">text</font></a>
- /// a red text decoration
- #[cfg(feature = "gecko")]
- const COLOR_OVERRIDE = 0x10;
- }
-}
-
-impl Default for TextDecorationLine {
- fn default() -> Self {
- TextDecorationLine::NONE
- }
-}
-
-impl TextDecorationLine {
- #[inline]
- /// Returns the initial value of text-decoration-line
- pub fn none() -> Self {
- TextDecorationLine::NONE
- }
-}
-
-#[derive(
- Clone,
- Copy,
- Debug,
- Eq,
- MallocSizeOf,
- PartialEq,
- SpecifiedValueInfo,
- ToComputedValue,
- ToResolvedValue,
- ToShmem,
-)]
-#[repr(C)]
-/// Specified value of the text-transform property, stored in two parts:
-/// the case-related transforms (mutually exclusive, only one may be in effect), and others (non-exclusive).
-pub struct TextTransform {
- /// Case transform, if any.
- pub case_: TextTransformCase,
- /// Non-case transforms.
- pub other_: TextTransformOther,
-}
-
-impl TextTransform {
- #[inline]
- /// Returns the initial value of text-transform
- pub fn none() -> Self {
- TextTransform {
- case_: TextTransformCase::None,
- other_: TextTransformOther::empty(),
- }
- }
- #[inline]
- /// Returns whether the value is 'none'
- pub fn is_none(&self) -> bool {
- self.case_ == TextTransformCase::None && self.other_.is_empty()
- }
-}
-
-// TODO: This can be simplified by deriving it.
-impl Parse for TextTransform {
- fn parse<'i, 't>(
- _context: &ParserContext,
- input: &mut Parser<'i, 't>,
- ) -> Result<Self, ParseError<'i>> {
- let mut result = TextTransform::none();
-
- // Case keywords are mutually exclusive; other transforms may co-occur.
- loop {
- let location = input.current_source_location();
- let ident = match input.next() {
- Ok(&Token::Ident(ref ident)) => ident,
- Ok(other) => return Err(location.new_unexpected_token_error(other.clone())),
- Err(..) => break,
- };
-
- match_ignore_ascii_case! { ident,
- "none" if result.is_none() => {
- return Ok(result);
- },
- "uppercase" if result.case_ == TextTransformCase::None => {
- result.case_ = TextTransformCase::Uppercase
- },
- "lowercase" if result.case_ == TextTransformCase::None => {
- result.case_ = TextTransformCase::Lowercase
- },
- "capitalize" if result.case_ == TextTransformCase::None => {
- result.case_ = TextTransformCase::Capitalize
- },
- "full-width" if !result.other_.intersects(TextTransformOther::FULL_WIDTH) => {
- result.other_.insert(TextTransformOther::FULL_WIDTH)
- },
- "full-size-kana" if !result.other_.intersects(TextTransformOther::FULL_SIZE_KANA) => {
- result.other_.insert(TextTransformOther::FULL_SIZE_KANA)
- },
- _ => return Err(location.new_custom_error(
- SelectorParseErrorKind::UnexpectedIdent(ident.clone())
- )),
- }
- }
-
- if result.is_none() {
- Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError))
- } else {
- Ok(result)
- }
- }
-}
-
-impl ToCss for TextTransform {
- fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
- where
- W: Write,
- {
- if self.is_none() {
- return dest.write_str("none");
- }
-
- if self.case_ != TextTransformCase::None {
- self.case_.to_css(dest)?;
- if !self.other_.is_empty() {
- dest.write_char(' ')?;
- }
- }
-
- self.other_.to_css(dest)
- }
-}
-
-#[derive(
- Clone,
- Copy,
- Debug,
- Eq,
- MallocSizeOf,
- PartialEq,
- SpecifiedValueInfo,
- ToComputedValue,
- ToCss,
- ToResolvedValue,
- ToShmem,
-)]
-#[repr(C)]
-/// Specified keyword values for case transforms in the text-transform property. (These are exclusive.)
-pub enum TextTransformCase {
- /// No case transform.
- None,
- /// All uppercase.
- Uppercase,
- /// All lowercase.
- Lowercase,
- /// Capitalize each word.
- Capitalize,
-}
-
-bitflags! {
- #[derive(MallocSizeOf, SpecifiedValueInfo, ToComputedValue, ToResolvedValue, ToShmem)]
- #[value_info(other_values = "none,full-width,full-size-kana")]
- #[repr(C)]
- /// Specified keyword values for non-case transforms in the text-transform property. (Non-exclusive.)
- pub struct TextTransformOther: u8 {
- /// full-width
- const FULL_WIDTH = 1 << 0;
- /// full-size-kana
- const FULL_SIZE_KANA = 1 << 1;
- }
-}
-
-impl ToCss for TextTransformOther {
- fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
- where
- W: Write,
- {
- let mut writer = SequenceWriter::new(dest, " ");
- let mut any = false;
- macro_rules! maybe_write {
- ($ident:ident => $str:expr) => {
- if self.contains(TextTransformOther::$ident) {
- writer.raw_item($str)?;
- any = true;
- }
- };
- }
-
- maybe_write!(FULL_WIDTH => "full-width");
- maybe_write!(FULL_SIZE_KANA => "full-size-kana");
-
- debug_assert!(any || self.is_empty());
-
- Ok(())
- }
-}
-
-/// Specified and computed value of text-align-last.
-#[derive(
- Clone,
- Copy,
- Debug,
- Eq,
- FromPrimitive,
- Hash,
- MallocSizeOf,
- Parse,
- PartialEq,
- SpecifiedValueInfo,
- ToComputedValue,
- ToCss,
- ToResolvedValue,
- ToShmem,
-)]
-#[allow(missing_docs)]
-#[repr(u8)]
-pub enum TextAlignLast {
- Auto,
- Start,
- End,
- Left,
- Right,
- Center,
- Justify,
-}
-
-/// Specified value of text-align keyword value.
-#[derive(
- Clone,
- Copy,
- Debug,
- Eq,
- FromPrimitive,
- Hash,
- MallocSizeOf,
- Parse,
- PartialEq,
- SpecifiedValueInfo,
- ToComputedValue,
- ToCss,
- ToResolvedValue,
- ToShmem,
-)]
-#[allow(missing_docs)]
-#[repr(u8)]
-pub enum TextAlignKeyword {
- Start,
- Left,
- Right,
- Center,
- Justify,
- #[css(skip)]
- #[cfg(feature = "gecko")]
- Char,
- End,
- #[cfg(feature = "gecko")]
- MozCenter,
- #[cfg(feature = "gecko")]
- MozLeft,
- #[cfg(feature = "gecko")]
- MozRight,
- #[cfg(feature = "servo")]
- ServoCenter,
- #[cfg(feature = "servo")]
- ServoLeft,
- #[cfg(feature = "servo")]
- ServoRight,
-}
-
-/// Specified value of text-align property.
-#[derive(
- Clone, Copy, Debug, Eq, Hash, MallocSizeOf, Parse, PartialEq, SpecifiedValueInfo, ToCss, ToShmem,
-)]
-pub enum TextAlign {
- /// Keyword value of text-align property.
- Keyword(TextAlignKeyword),
- /// `match-parent` value of text-align property. It has a different handling
- /// unlike other keywords.
- #[cfg(feature = "gecko")]
- MatchParent,
- /// This is how we implement the following HTML behavior from
- /// https://html.spec.whatwg.org/#tables-2:
- ///
- /// User agents are expected to have a rule in their user agent style sheet
- /// that matches th elements that have a parent node whose computed value
- /// for the 'text-align' property is its initial value, whose declaration
- /// block consists of just a single declaration that sets the 'text-align'
- /// property to the value 'center'.
- ///
- /// Since selectors can't depend on the ancestor styles, we implement it with a
- /// magic value that computes to the right thing. Since this is an
- /// implementation detail, it shouldn't be exposed to web content.
- #[cfg(feature = "gecko")]
- #[parse(condition = "ParserContext::in_ua_or_chrome_sheet")]
- MozCenterOrInherit,
-}
-
-impl ToComputedValue for TextAlign {
- type ComputedValue = TextAlignKeyword;
-
- #[inline]
- fn to_computed_value(&self, _context: &Context) -> Self::ComputedValue {
- match *self {
- TextAlign::Keyword(key) => key,
- #[cfg(feature = "gecko")]
- TextAlign::MatchParent => {
- // on the root <html> element we should still respect the dir
- // but the parent dir of that element is LTR even if it's <html dir=rtl>
- // and will only be RTL if certain prefs have been set.
- // In that case, the default behavior here will set it to left,
- // but we want to set it to right -- instead set it to the default (`start`),
- // which will do the right thing in this case (but not the general case)
- if _context.builder.is_root_element {
- return TextAlignKeyword::Start;
- }
- let parent = _context
- .builder
- .get_parent_inherited_text()
- .clone_text_align();
- let ltr = _context.builder.inherited_writing_mode().is_bidi_ltr();
- match (parent, ltr) {
- (TextAlignKeyword::Start, true) => TextAlignKeyword::Left,
- (TextAlignKeyword::Start, false) => TextAlignKeyword::Right,
- (TextAlignKeyword::End, true) => TextAlignKeyword::Right,
- (TextAlignKeyword::End, false) => TextAlignKeyword::Left,
- _ => parent,
- }
- },
- #[cfg(feature = "gecko")]
- TextAlign::MozCenterOrInherit => {
- let parent = _context
- .builder
- .get_parent_inherited_text()
- .clone_text_align();
- if parent == TextAlignKeyword::Start {
- TextAlignKeyword::Center
- } else {
- parent
- }
- },
- }
- }
-
- #[inline]
- fn from_computed_value(computed: &Self::ComputedValue) -> Self {
- TextAlign::Keyword(*computed)
- }
-}
-
-fn fill_mode_is_default_and_shape_exists(
- fill: &TextEmphasisFillMode,
- shape: &Option<TextEmphasisShapeKeyword>,
-) -> bool {
- shape.is_some() && fill.is_filled()
-}
-
-/// Specified value of text-emphasis-style property.
-///
-/// https://drafts.csswg.org/css-text-decor/#propdef-text-emphasis-style
-#[derive(Clone, Debug, MallocSizeOf, PartialEq, SpecifiedValueInfo, ToCss, ToShmem)]
-#[allow(missing_docs)]
-pub enum TextEmphasisStyle {
- /// [ <fill> || <shape> ]
- Keyword {
- #[css(contextual_skip_if = "fill_mode_is_default_and_shape_exists")]
- fill: TextEmphasisFillMode,
- shape: Option<TextEmphasisShapeKeyword>,
- },
- /// `none`
- None,
- /// `<string>` (of which only the first grapheme cluster will be used).
- String(crate::OwnedStr),
-}
-
-/// Fill mode for the text-emphasis-style property
-#[derive(
- Clone,
- Copy,
- Debug,
- MallocSizeOf,
- Parse,
- PartialEq,
- SpecifiedValueInfo,
- ToCss,
- ToComputedValue,
- ToResolvedValue,
- ToShmem,
-)]
-#[repr(u8)]
-pub enum TextEmphasisFillMode {
- /// `filled`
- Filled,
- /// `open`
- Open,
-}
-
-impl TextEmphasisFillMode {
- /// Whether the value is `filled`.
- #[inline]
- pub fn is_filled(&self) -> bool {
- matches!(*self, TextEmphasisFillMode::Filled)
- }
-}
-
-/// Shape keyword for the text-emphasis-style property
-#[derive(
- Clone,
- Copy,
- Debug,
- Eq,
- MallocSizeOf,
- Parse,
- PartialEq,
- SpecifiedValueInfo,
- ToCss,
- ToComputedValue,
- ToResolvedValue,
- ToShmem,
-)]
-#[repr(u8)]
-pub enum TextEmphasisShapeKeyword {
- /// `dot`
- Dot,
- /// `circle`
- Circle,
- /// `double-circle`
- DoubleCircle,
- /// `triangle`
- Triangle,
- /// `sesame`
- Sesame,
-}
-
-impl ToComputedValue for TextEmphasisStyle {
- type ComputedValue = ComputedTextEmphasisStyle;
-
- #[inline]
- fn to_computed_value(&self, context: &Context) -> Self::ComputedValue {
- match *self {
- TextEmphasisStyle::Keyword { fill, shape } => {
- let shape = shape.unwrap_or_else(|| {
- // FIXME(emilio, bug 1572958): This should set the
- // rule_cache_conditions properly.
- //
- // Also should probably use WritingMode::is_vertical rather
- // than the computed value of the `writing-mode` property.
- if context.style().get_inherited_box().clone_writing_mode() ==
- SpecifiedWritingMode::HorizontalTb
- {
- TextEmphasisShapeKeyword::Circle
- } else {
- TextEmphasisShapeKeyword::Sesame
- }
- });
- ComputedTextEmphasisStyle::Keyword { fill, shape }
- },
- TextEmphasisStyle::None => ComputedTextEmphasisStyle::None,
- TextEmphasisStyle::String(ref s) => {
- // Passing `true` to iterate over extended grapheme clusters, following
- // recommendation at http://www.unicode.org/reports/tr29/#Grapheme_Cluster_Boundaries
- //
- // FIXME(emilio): Doing this at computed value time seems wrong.
- // The spec doesn't say that this should be a computed-value
- // time operation. This is observable from getComputedStyle().
- let s = s.graphemes(true).next().unwrap_or("").to_string();
- ComputedTextEmphasisStyle::String(s.into())
- },
- }
- }
-
- #[inline]
- fn from_computed_value(computed: &Self::ComputedValue) -> Self {
- match *computed {
- ComputedTextEmphasisStyle::Keyword { fill, shape } => TextEmphasisStyle::Keyword {
- fill,
- shape: Some(shape),
- },
- ComputedTextEmphasisStyle::None => TextEmphasisStyle::None,
- ComputedTextEmphasisStyle::String(ref string) => {
- TextEmphasisStyle::String(string.clone())
- },
- }
- }
-}
-
-impl Parse for TextEmphasisStyle {
- fn parse<'i, 't>(
- _context: &ParserContext,
- input: &mut Parser<'i, 't>,
- ) -> Result<Self, ParseError<'i>> {
- if input
- .try_parse(|input| input.expect_ident_matching("none"))
- .is_ok()
- {
- return Ok(TextEmphasisStyle::None);
- }
-
- if let Ok(s) = input.try_parse(|i| i.expect_string().map(|s| s.as_ref().to_owned())) {
- // Handle <string>
- return Ok(TextEmphasisStyle::String(s.into()));
- }
-
- // Handle a pair of keywords
- let mut shape = input.try_parse(TextEmphasisShapeKeyword::parse).ok();
- let fill = input.try_parse(TextEmphasisFillMode::parse).ok();
- if shape.is_none() {
- shape = input.try_parse(TextEmphasisShapeKeyword::parse).ok();
- }
-
- if shape.is_none() && fill.is_none() {
- return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError));
- }
-
- // If a shape keyword is specified but neither filled nor open is
- // specified, filled is assumed.
- let fill = fill.unwrap_or(TextEmphasisFillMode::Filled);
-
- // We cannot do the same because the default `<shape>` depends on the
- // computed writing-mode.
- Ok(TextEmphasisStyle::Keyword { fill, shape })
- }
-}
-
-bitflags! {
- #[derive(MallocSizeOf, SpecifiedValueInfo, ToComputedValue, ToResolvedValue, ToShmem, Parse, ToCss)]
- #[repr(C)]
- #[css(bitflags(mixed="over,under,left,right", validate_mixed="Self::validate_and_simplify"))]
- /// Values for text-emphasis-position:
- /// <https://drafts.csswg.org/css-text-decor/#text-emphasis-position-property>
- pub struct TextEmphasisPosition: u8 {
- /// Draws marks to the right of the text in vertical writing mode.
- const OVER = 1 << 0;
- /// Draw marks under the text in horizontal writing mode.
- const UNDER = 1 << 1;
- /// Draw marks to the left of the text in vertical writing mode.
- const LEFT = 1 << 2;
- /// Draws marks to the right of the text in vertical writing mode.
- const RIGHT = 1 << 3;
- }
-}
-
-impl TextEmphasisPosition {
- fn validate_and_simplify(&mut self) -> bool {
- if self.intersects(Self::OVER) == self.intersects(Self::UNDER) {
- return false;
- }
-
- if self.intersects(Self::LEFT) {
- return !self.intersects(Self::RIGHT);
- }
-
- self.remove(Self::RIGHT); // Right is the default
- true
- }
-}
-
-/// Values for the `word-break` property.
-#[repr(u8)]
-#[derive(
- Clone,
- Copy,
- Debug,
- Eq,
- MallocSizeOf,
- Parse,
- PartialEq,
- SpecifiedValueInfo,
- ToComputedValue,
- ToCss,
- ToResolvedValue,
- ToShmem,
-)]
-#[allow(missing_docs)]
-pub enum WordBreak {
- Normal,
- BreakAll,
- KeepAll,
- /// The break-word value, needed for compat.
- ///
- /// Specifying `word-break: break-word` makes `overflow-wrap` behave as
- /// `anywhere`, and `word-break` behave like `normal`.
- #[cfg(feature = "gecko")]
- BreakWord,
-}
-
-/// Values for the `text-justify` CSS property.
-#[repr(u8)]
-#[derive(
- Clone,
- Copy,
- Debug,
- Eq,
- MallocSizeOf,
- Parse,
- PartialEq,
- SpecifiedValueInfo,
- ToComputedValue,
- ToCss,
- ToResolvedValue,
- ToShmem,
-)]
-#[allow(missing_docs)]
-pub enum TextJustify {
- Auto,
- None,
- InterWord,
- // See https://drafts.csswg.org/css-text-3/#valdef-text-justify-distribute
- // and https://github.com/w3c/csswg-drafts/issues/6156 for the alias.
- #[parse(aliases = "distribute")]
- InterCharacter,
-}
-
-/// Values for the `-moz-control-character-visibility` CSS property.
-#[repr(u8)]
-#[derive(
- Clone,
- Copy,
- Debug,
- Eq,
- MallocSizeOf,
- Parse,
- PartialEq,
- SpecifiedValueInfo,
- ToComputedValue,
- ToCss,
- ToResolvedValue,
- ToShmem,
-)]
-#[allow(missing_docs)]
-pub enum MozControlCharacterVisibility {
- Hidden,
- Visible,
-}
-
-#[cfg(feature = "gecko")]
-impl Default for MozControlCharacterVisibility {
- fn default() -> Self {
- if static_prefs::pref!("layout.css.control-characters.visible") {
- Self::Visible
- } else {
- Self::Hidden
- }
- }
-}
-
-/// Values for the `line-break` property.
-#[repr(u8)]
-#[derive(
- Clone,
- Copy,
- Debug,
- Eq,
- MallocSizeOf,
- Parse,
- PartialEq,
- SpecifiedValueInfo,
- ToComputedValue,
- ToCss,
- ToResolvedValue,
- ToShmem,
-)]
-#[allow(missing_docs)]
-pub enum LineBreak {
- Auto,
- Loose,
- Normal,
- Strict,
- Anywhere,
-}
-
-/// Values for the `overflow-wrap` property.
-#[repr(u8)]
-#[derive(
- Clone,
- Copy,
- Debug,
- Eq,
- MallocSizeOf,
- Parse,
- PartialEq,
- SpecifiedValueInfo,
- ToComputedValue,
- ToCss,
- ToResolvedValue,
- ToShmem,
-)]
-#[allow(missing_docs)]
-pub enum OverflowWrap {
- Normal,
- BreakWord,
- Anywhere,
-}
-
-/// Implements text-decoration-skip-ink which takes the keywords auto | none | all
-///
-/// https://drafts.csswg.org/css-text-decor-4/#text-decoration-skip-ink-property
-#[repr(u8)]
-#[cfg_attr(feature = "servo", derive(Deserialize, Serialize))]
-#[derive(
- Clone,
- Copy,
- Debug,
- Eq,
- MallocSizeOf,
- Parse,
- PartialEq,
- SpecifiedValueInfo,
- ToComputedValue,
- ToCss,
- ToResolvedValue,
- ToShmem,
-)]
-#[allow(missing_docs)]
-pub enum TextDecorationSkipInk {
- Auto,
- None,
- All,
-}
-
-/// Implements type for `text-decoration-thickness` property
-pub type TextDecorationLength = GenericTextDecorationLength<LengthPercentage>;
-
-impl TextDecorationLength {
- /// `Auto` value.
- #[inline]
- pub fn auto() -> Self {
- GenericTextDecorationLength::Auto
- }
-
- /// Whether this is the `Auto` value.
- #[inline]
- pub fn is_auto(&self) -> bool {
- matches!(*self, GenericTextDecorationLength::Auto)
- }
-}
-
-bitflags! {
- #[derive(MallocSizeOf, SpecifiedValueInfo, ToComputedValue, ToResolvedValue, ToShmem)]
- #[value_info(other_values = "auto,from-font,under,left,right")]
- #[repr(C)]
- /// Specified keyword values for the text-underline-position property.
- /// (Non-exclusive, but not all combinations are allowed: the spec grammar gives
- /// `auto | [ from-font | under ] || [ left | right ]`.)
- /// https://drafts.csswg.org/css-text-decor-4/#text-underline-position-property
- pub struct TextUnderlinePosition: u8 {
- /// Use automatic positioning below the alphabetic baseline.
- const AUTO = 0;
- /// Use underline position from the first available font.
- const FROM_FONT = 1 << 0;
- /// Below the glyph box.
- const UNDER = 1 << 1;
- /// In vertical mode, place to the left of the text.
- const LEFT = 1 << 2;
- /// In vertical mode, place to the right of the text.
- const RIGHT = 1 << 3;
- }
-}
-
-// TODO: This can be derived with some care.
-impl Parse for TextUnderlinePosition {
- fn parse<'i, 't>(
- _context: &ParserContext,
- input: &mut Parser<'i, 't>,
- ) -> Result<TextUnderlinePosition, ParseError<'i>> {
- let mut result = TextUnderlinePosition::empty();
-
- loop {
- let location = input.current_source_location();
- let ident = match input.next() {
- Ok(&Token::Ident(ref ident)) => ident,
- Ok(other) => return Err(location.new_unexpected_token_error(other.clone())),
- Err(..) => break,
- };
-
- match_ignore_ascii_case! { ident,
- "auto" if result.is_empty() => {
- return Ok(result);
- },
- "from-font" if !result.intersects(TextUnderlinePosition::FROM_FONT |
- TextUnderlinePosition::UNDER) => {
- result.insert(TextUnderlinePosition::FROM_FONT);
- },
- "under" if !result.intersects(TextUnderlinePosition::FROM_FONT |
- TextUnderlinePosition::UNDER) => {
- result.insert(TextUnderlinePosition::UNDER);
- },
- "left" if !result.intersects(TextUnderlinePosition::LEFT |
- TextUnderlinePosition::RIGHT) => {
- result.insert(TextUnderlinePosition::LEFT);
- },
- "right" if !result.intersects(TextUnderlinePosition::LEFT |
- TextUnderlinePosition::RIGHT) => {
- result.insert(TextUnderlinePosition::RIGHT);
- },
- _ => return Err(location.new_custom_error(
- SelectorParseErrorKind::UnexpectedIdent(ident.clone())
- )),
- }
- }
-
- if !result.is_empty() {
- Ok(result)
- } else {
- Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError))
- }
- }
-}
-
-impl ToCss for TextUnderlinePosition {
- fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
- where
- W: Write,
- {
- if self.is_empty() {
- return dest.write_str("auto");
- }
-
- let mut writer = SequenceWriter::new(dest, " ");
- let mut any = false;
-
- macro_rules! maybe_write {
- ($ident:ident => $str:expr) => {
- if self.contains(TextUnderlinePosition::$ident) {
- any = true;
- writer.raw_item($str)?;
- }
- };
- }
-
- maybe_write!(FROM_FONT => "from-font");
- maybe_write!(UNDER => "under");
- maybe_write!(LEFT => "left");
- maybe_write!(RIGHT => "right");
-
- debug_assert!(any);
-
- Ok(())
- }
-}
-
-/// Values for `ruby-position` property
-#[repr(u8)]
-#[derive(
- Clone, Copy, Debug, Eq, MallocSizeOf, PartialEq, ToComputedValue, ToResolvedValue, ToShmem,
-)]
-#[allow(missing_docs)]
-pub enum RubyPosition {
- AlternateOver,
- AlternateUnder,
- Over,
- Under,
-}
-
-impl Parse for RubyPosition {
- fn parse<'i, 't>(
- _context: &ParserContext,
- input: &mut Parser<'i, 't>,
- ) -> Result<RubyPosition, ParseError<'i>> {
- // Parse alternate before
- let alternate = input
- .try_parse(|i| i.expect_ident_matching("alternate"))
- .is_ok();
- if alternate && input.is_exhausted() {
- return Ok(RubyPosition::AlternateOver);
- }
- // Parse over / under
- let over = try_match_ident_ignore_ascii_case! { input,
- "over" => true,
- "under" => false,
- };
- // Parse alternate after
- let alternate = alternate ||
- input
- .try_parse(|i| i.expect_ident_matching("alternate"))
- .is_ok();
-
- Ok(match (over, alternate) {
- (true, true) => RubyPosition::AlternateOver,
- (false, true) => RubyPosition::AlternateUnder,
- (true, false) => RubyPosition::Over,
- (false, false) => RubyPosition::Under,
- })
- }
-}
-
-impl ToCss for RubyPosition {
- fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
- where
- W: Write,
- {
- dest.write_str(match self {
- RubyPosition::AlternateOver => "alternate",
- RubyPosition::AlternateUnder => "alternate under",
- RubyPosition::Over => "over",
- RubyPosition::Under => "under",
- })
- }
-}
-
-impl SpecifiedValueInfo for RubyPosition {
- fn collect_completion_keywords(f: KeywordsCollectFn) {
- f(&["alternate", "over", "under"])
- }
-}
diff --git a/components/style/values/specified/time.rs b/components/style/values/specified/time.rs
deleted file mode 100644
index e8410c7f43d..00000000000
--- a/components/style/values/specified/time.rs
+++ /dev/null
@@ -1,174 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
-
-//! Specified time values.
-
-use crate::parser::{Parse, ParserContext};
-use crate::values::computed::time::Time as ComputedTime;
-use crate::values::computed::{Context, ToComputedValue};
-use crate::values::specified::calc::CalcNode;
-use crate::values::CSSFloat;
-use cssparser::{Parser, Token};
-use std::fmt::{self, Write};
-use style_traits::values::specified::AllowedNumericType;
-use style_traits::{CssWriter, ParseError, SpecifiedValueInfo, StyleParseErrorKind, ToCss};
-
-/// A time value according to CSS-VALUES § 6.2.
-#[derive(Clone, Copy, Debug, MallocSizeOf, PartialEq, ToShmem)]
-pub struct Time {
- seconds: CSSFloat,
- unit: TimeUnit,
- calc_clamping_mode: Option<AllowedNumericType>,
-}
-
-/// A time unit.
-#[derive(Clone, Copy, Debug, Eq, MallocSizeOf, PartialEq, ToShmem)]
-pub enum TimeUnit {
- /// `s`
- Second,
- /// `ms`
- Millisecond,
-}
-
-impl Time {
- /// Returns a time value that represents `seconds` seconds.
- pub fn from_seconds_with_calc_clamping_mode(
- seconds: CSSFloat,
- calc_clamping_mode: Option<AllowedNumericType>,
- ) -> Self {
- Time {
- seconds,
- unit: TimeUnit::Second,
- calc_clamping_mode,
- }
- }
-
- /// Returns a time value that represents `seconds` seconds.
- pub fn from_seconds(seconds: CSSFloat) -> Self {
- Self::from_seconds_with_calc_clamping_mode(seconds, None)
- }
-
- /// Returns `0s`.
- pub fn zero() -> Self {
- Self::from_seconds(0.0)
- }
-
- /// Returns the time in fractional seconds.
- pub fn seconds(self) -> CSSFloat {
- self.seconds
- }
-
- /// Returns the unit of the time.
- #[inline]
- pub fn unit(&self) -> &'static str {
- match self.unit {
- TimeUnit::Second => "s",
- TimeUnit::Millisecond => "ms",
- }
- }
-
- #[inline]
- fn unitless_value(&self) -> CSSFloat {
- match self.unit {
- TimeUnit::Second => self.seconds,
- TimeUnit::Millisecond => self.seconds * 1000.,
- }
- }
-
- /// Parses a time according to CSS-VALUES § 6.2.
- pub fn parse_dimension(value: CSSFloat, unit: &str) -> Result<Time, ()> {
- let (seconds, unit) = match_ignore_ascii_case! { unit,
- "s" => (value, TimeUnit::Second),
- "ms" => (value / 1000.0, TimeUnit::Millisecond),
- _ => return Err(())
- };
-
- Ok(Time {
- seconds,
- unit,
- calc_clamping_mode: None,
- })
- }
-
- fn parse_with_clamping_mode<'i, 't>(
- context: &ParserContext,
- input: &mut Parser<'i, 't>,
- clamping_mode: AllowedNumericType,
- ) -> Result<Self, ParseError<'i>> {
- use style_traits::ParsingMode;
-
- let location = input.current_source_location();
- match *input.next()? {
- // Note that we generally pass ParserContext to is_ok() to check
- // that the ParserMode of the ParserContext allows all numeric
- // values for SMIL regardless of clamping_mode, but in this Time
- // value case, the value does not animate for SMIL at all, so we use
- // ParsingMode::DEFAULT directly.
- Token::Dimension {
- value, ref unit, ..
- } if clamping_mode.is_ok(ParsingMode::DEFAULT, value) => {
- Time::parse_dimension(value, unit)
- .map_err(|()| location.new_custom_error(StyleParseErrorKind::UnspecifiedError))
- },
- Token::Function(ref name) => {
- let function = CalcNode::math_function(context, name, location)?;
- CalcNode::parse_time(context, input, clamping_mode, function)
- },
- ref t => return Err(location.new_unexpected_token_error(t.clone())),
- }
- }
-
- /// Parses a non-negative time value.
- pub fn parse_non_negative<'i, 't>(
- context: &ParserContext,
- input: &mut Parser<'i, 't>,
- ) -> Result<Self, ParseError<'i>> {
- Self::parse_with_clamping_mode(context, input, AllowedNumericType::NonNegative)
- }
-}
-
-impl ToComputedValue for Time {
- type ComputedValue = ComputedTime;
-
- fn to_computed_value(&self, _context: &Context) -> Self::ComputedValue {
- let seconds = self
- .calc_clamping_mode
- .map_or(self.seconds(), |mode| mode.clamp(self.seconds()));
-
- ComputedTime::from_seconds(crate::values::normalize(seconds))
- }
-
- fn from_computed_value(computed: &Self::ComputedValue) -> Self {
- Time {
- seconds: computed.seconds(),
- unit: TimeUnit::Second,
- calc_clamping_mode: None,
- }
- }
-}
-
-impl Parse for Time {
- fn parse<'i, 't>(
- context: &ParserContext,
- input: &mut Parser<'i, 't>,
- ) -> Result<Self, ParseError<'i>> {
- Self::parse_with_clamping_mode(context, input, AllowedNumericType::All)
- }
-}
-
-impl ToCss for Time {
- fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
- where
- W: Write,
- {
- crate::values::serialize_specified_dimension(
- self.unitless_value(),
- self.unit(),
- self.calc_clamping_mode.is_some(),
- dest,
- )
- }
-}
-
-impl SpecifiedValueInfo for Time {}
diff --git a/components/style/values/specified/transform.rs b/components/style/values/specified/transform.rs
deleted file mode 100644
index c10b79a089b..00000000000
--- a/components/style/values/specified/transform.rs
+++ /dev/null
@@ -1,487 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
-
-//! Specified types for CSS values that are related to transformations.
-
-use crate::parser::{Parse, ParserContext};
-use crate::values::computed::{Context, LengthPercentage as ComputedLengthPercentage};
-use crate::values::computed::{Percentage as ComputedPercentage, ToComputedValue};
-use crate::values::generics::transform as generic;
-use crate::values::generics::transform::{Matrix, Matrix3D};
-use crate::values::specified::position::{
- HorizontalPositionKeyword, Side, VerticalPositionKeyword,
-};
-use crate::values::specified::{
- self, Angle, Integer, Length, LengthPercentage, Number, NumberOrPercentage,
-};
-use crate::Zero;
-use cssparser::Parser;
-use style_traits::{ParseError, StyleParseErrorKind};
-
-pub use crate::values::generics::transform::TransformStyle;
-
-/// A single operation in a specified CSS `transform`
-pub type TransformOperation =
- generic::TransformOperation<Angle, Number, Length, Integer, LengthPercentage>;
-
-/// A specified CSS `transform`
-pub type Transform = generic::Transform<TransformOperation>;
-
-/// The specified value of a CSS `<transform-origin>`
-pub type TransformOrigin = generic::TransformOrigin<
- OriginComponent<HorizontalPositionKeyword>,
- OriginComponent<VerticalPositionKeyword>,
- Length,
->;
-
-impl TransformOrigin {
- /// Returns the initial specified value for `transform-origin`.
- #[inline]
- pub fn initial_value() -> Self {
- Self::new(
- OriginComponent::Length(LengthPercentage::Percentage(ComputedPercentage(0.5))),
- OriginComponent::Length(LengthPercentage::Percentage(ComputedPercentage(0.5))),
- Length::zero(),
- )
- }
-
- /// Returns the `0 0` value.
- pub fn zero_zero() -> Self {
- Self::new(
- OriginComponent::Length(LengthPercentage::zero()),
- OriginComponent::Length(LengthPercentage::zero()),
- Length::zero(),
- )
- }
-}
-
-impl Transform {
- /// Internal parse function for deciding if we wish to accept prefixed values or not
- ///
- /// `transform` allows unitless zero angles as an exception, see:
- /// https://github.com/w3c/csswg-drafts/issues/1162
- fn parse_internal<'i, 't>(
- context: &ParserContext,
- input: &mut Parser<'i, 't>,
- ) -> Result<Self, ParseError<'i>> {
- use style_traits::{Separator, Space};
-
- if input
- .try_parse(|input| input.expect_ident_matching("none"))
- .is_ok()
- {
- return Ok(generic::Transform::none());
- }
-
- Ok(generic::Transform(
- Space::parse(input, |input| {
- let function = input.expect_function()?.clone();
- input.parse_nested_block(|input| {
- let location = input.current_source_location();
- let result = match_ignore_ascii_case! { &function,
- "matrix" => {
- let a = Number::parse(context, input)?;
- input.expect_comma()?;
- let b = Number::parse(context, input)?;
- input.expect_comma()?;
- let c = Number::parse(context, input)?;
- input.expect_comma()?;
- let d = Number::parse(context, input)?;
- input.expect_comma()?;
- // Standard matrix parsing.
- let e = Number::parse(context, input)?;
- input.expect_comma()?;
- let f = Number::parse(context, input)?;
- Ok(generic::TransformOperation::Matrix(Matrix { a, b, c, d, e, f }))
- },
- "matrix3d" => {
- let m11 = Number::parse(context, input)?;
- input.expect_comma()?;
- let m12 = Number::parse(context, input)?;
- input.expect_comma()?;
- let m13 = Number::parse(context, input)?;
- input.expect_comma()?;
- let m14 = Number::parse(context, input)?;
- input.expect_comma()?;
- let m21 = Number::parse(context, input)?;
- input.expect_comma()?;
- let m22 = Number::parse(context, input)?;
- input.expect_comma()?;
- let m23 = Number::parse(context, input)?;
- input.expect_comma()?;
- let m24 = Number::parse(context, input)?;
- input.expect_comma()?;
- let m31 = Number::parse(context, input)?;
- input.expect_comma()?;
- let m32 = Number::parse(context, input)?;
- input.expect_comma()?;
- let m33 = Number::parse(context, input)?;
- input.expect_comma()?;
- let m34 = Number::parse(context, input)?;
- input.expect_comma()?;
- // Standard matrix3d parsing.
- let m41 = Number::parse(context, input)?;
- input.expect_comma()?;
- let m42 = Number::parse(context, input)?;
- input.expect_comma()?;
- let m43 = Number::parse(context, input)?;
- input.expect_comma()?;
- let m44 = Number::parse(context, input)?;
- Ok(generic::TransformOperation::Matrix3D(Matrix3D {
- m11, m12, m13, m14,
- m21, m22, m23, m24,
- m31, m32, m33, m34,
- m41, m42, m43, m44,
- }))
- },
- "translate" => {
- let sx = specified::LengthPercentage::parse(context, input)?;
- if input.try_parse(|input| input.expect_comma()).is_ok() {
- let sy = specified::LengthPercentage::parse(context, input)?;
- Ok(generic::TransformOperation::Translate(sx, sy))
- } else {
- Ok(generic::TransformOperation::Translate(sx, Zero::zero()))
- }
- },
- "translatex" => {
- let tx = specified::LengthPercentage::parse(context, input)?;
- Ok(generic::TransformOperation::TranslateX(tx))
- },
- "translatey" => {
- let ty = specified::LengthPercentage::parse(context, input)?;
- Ok(generic::TransformOperation::TranslateY(ty))
- },
- "translatez" => {
- let tz = specified::Length::parse(context, input)?;
- Ok(generic::TransformOperation::TranslateZ(tz))
- },
- "translate3d" => {
- let tx = specified::LengthPercentage::parse(context, input)?;
- input.expect_comma()?;
- let ty = specified::LengthPercentage::parse(context, input)?;
- input.expect_comma()?;
- let tz = specified::Length::parse(context, input)?;
- Ok(generic::TransformOperation::Translate3D(tx, ty, tz))
- },
- "scale" => {
- let sx = NumberOrPercentage::parse(context, input)?.to_number();
- if input.try_parse(|input| input.expect_comma()).is_ok() {
- let sy = NumberOrPercentage::parse(context, input)?.to_number();
- Ok(generic::TransformOperation::Scale(sx, sy))
- } else {
- Ok(generic::TransformOperation::Scale(sx, sx))
- }
- },
- "scalex" => {
- let sx = NumberOrPercentage::parse(context, input)?.to_number();
- Ok(generic::TransformOperation::ScaleX(sx))
- },
- "scaley" => {
- let sy = NumberOrPercentage::parse(context, input)?.to_number();
- Ok(generic::TransformOperation::ScaleY(sy))
- },
- "scalez" => {
- let sz = NumberOrPercentage::parse(context, input)?.to_number();
- Ok(generic::TransformOperation::ScaleZ(sz))
- },
- "scale3d" => {
- let sx = NumberOrPercentage::parse(context, input)?.to_number();
- input.expect_comma()?;
- let sy = NumberOrPercentage::parse(context, input)?.to_number();
- input.expect_comma()?;
- let sz = NumberOrPercentage::parse(context, input)?.to_number();
- Ok(generic::TransformOperation::Scale3D(sx, sy, sz))
- },
- "rotate" => {
- let theta = specified::Angle::parse_with_unitless(context, input)?;
- Ok(generic::TransformOperation::Rotate(theta))
- },
- "rotatex" => {
- let theta = specified::Angle::parse_with_unitless(context, input)?;
- Ok(generic::TransformOperation::RotateX(theta))
- },
- "rotatey" => {
- let theta = specified::Angle::parse_with_unitless(context, input)?;
- Ok(generic::TransformOperation::RotateY(theta))
- },
- "rotatez" => {
- let theta = specified::Angle::parse_with_unitless(context, input)?;
- Ok(generic::TransformOperation::RotateZ(theta))
- },
- "rotate3d" => {
- let ax = Number::parse(context, input)?;
- input.expect_comma()?;
- let ay = Number::parse(context, input)?;
- input.expect_comma()?;
- let az = Number::parse(context, input)?;
- input.expect_comma()?;
- let theta = specified::Angle::parse_with_unitless(context, input)?;
- // TODO(gw): Check that the axis can be normalized.
- Ok(generic::TransformOperation::Rotate3D(ax, ay, az, theta))
- },
- "skew" => {
- let ax = specified::Angle::parse_with_unitless(context, input)?;
- if input.try_parse(|input| input.expect_comma()).is_ok() {
- let ay = specified::Angle::parse_with_unitless(context, input)?;
- Ok(generic::TransformOperation::Skew(ax, ay))
- } else {
- Ok(generic::TransformOperation::Skew(ax, Zero::zero()))
- }
- },
- "skewx" => {
- let theta = specified::Angle::parse_with_unitless(context, input)?;
- Ok(generic::TransformOperation::SkewX(theta))
- },
- "skewy" => {
- let theta = specified::Angle::parse_with_unitless(context, input)?;
- Ok(generic::TransformOperation::SkewY(theta))
- },
- "perspective" => {
- let p = match input.try_parse(|input| specified::Length::parse_non_negative(context, input)) {
- Ok(p) => generic::PerspectiveFunction::Length(p),
- Err(..) => {
- input.expect_ident_matching("none")?;
- generic::PerspectiveFunction::None
- }
- };
- Ok(generic::TransformOperation::Perspective(p))
- },
- _ => Err(()),
- };
- result.map_err(|()| {
- location.new_custom_error(StyleParseErrorKind::UnexpectedFunction(
- function.clone(),
- ))
- })
- })
- })?
- .into(),
- ))
- }
-}
-
-impl Parse for Transform {
- fn parse<'i, 't>(
- context: &ParserContext,
- input: &mut Parser<'i, 't>,
- ) -> Result<Self, ParseError<'i>> {
- Transform::parse_internal(context, input)
- }
-}
-
-/// The specified value of a component of a CSS `<transform-origin>`.
-#[derive(Clone, Debug, MallocSizeOf, Parse, PartialEq, SpecifiedValueInfo, ToCss, ToShmem)]
-pub enum OriginComponent<S> {
- /// `center`
- Center,
- /// `<length-percentage>`
- Length(LengthPercentage),
- /// `<side>`
- Side(S),
-}
-
-impl Parse for TransformOrigin {
- fn parse<'i, 't>(
- context: &ParserContext,
- input: &mut Parser<'i, 't>,
- ) -> Result<Self, ParseError<'i>> {
- let parse_depth = |input: &mut Parser| {
- input
- .try_parse(|i| Length::parse(context, i))
- .unwrap_or(Length::zero())
- };
- match input.try_parse(|i| OriginComponent::parse(context, i)) {
- Ok(x_origin @ OriginComponent::Center) => {
- if let Ok(y_origin) = input.try_parse(|i| OriginComponent::parse(context, i)) {
- let depth = parse_depth(input);
- return Ok(Self::new(x_origin, y_origin, depth));
- }
- let y_origin = OriginComponent::Center;
- if let Ok(x_keyword) = input.try_parse(HorizontalPositionKeyword::parse) {
- let x_origin = OriginComponent::Side(x_keyword);
- let depth = parse_depth(input);
- return Ok(Self::new(x_origin, y_origin, depth));
- }
- let depth = Length::from_px(0.);
- return Ok(Self::new(x_origin, y_origin, depth));
- },
- Ok(x_origin) => {
- if let Ok(y_origin) = input.try_parse(|i| OriginComponent::parse(context, i)) {
- let depth = parse_depth(input);
- return Ok(Self::new(x_origin, y_origin, depth));
- }
- let y_origin = OriginComponent::Center;
- let depth = Length::from_px(0.);
- return Ok(Self::new(x_origin, y_origin, depth));
- },
- Err(_) => {},
- }
- let y_keyword = VerticalPositionKeyword::parse(input)?;
- let y_origin = OriginComponent::Side(y_keyword);
- if let Ok(x_keyword) = input.try_parse(HorizontalPositionKeyword::parse) {
- let x_origin = OriginComponent::Side(x_keyword);
- let depth = parse_depth(input);
- return Ok(Self::new(x_origin, y_origin, depth));
- }
- if input
- .try_parse(|i| i.expect_ident_matching("center"))
- .is_ok()
- {
- let x_origin = OriginComponent::Center;
- let depth = parse_depth(input);
- return Ok(Self::new(x_origin, y_origin, depth));
- }
- let x_origin = OriginComponent::Center;
- let depth = Length::from_px(0.);
- Ok(Self::new(x_origin, y_origin, depth))
- }
-}
-
-impl<S> ToComputedValue for OriginComponent<S>
-where
- S: Side,
-{
- type ComputedValue = ComputedLengthPercentage;
-
- fn to_computed_value(&self, context: &Context) -> Self::ComputedValue {
- match *self {
- OriginComponent::Center => {
- ComputedLengthPercentage::new_percent(ComputedPercentage(0.5))
- },
- OriginComponent::Length(ref length) => length.to_computed_value(context),
- OriginComponent::Side(ref keyword) => {
- let p = ComputedPercentage(if keyword.is_start() { 0. } else { 1. });
- ComputedLengthPercentage::new_percent(p)
- },
- }
- }
-
- fn from_computed_value(computed: &Self::ComputedValue) -> Self {
- OriginComponent::Length(ToComputedValue::from_computed_value(computed))
- }
-}
-
-impl<S> OriginComponent<S> {
- /// `0%`
- pub fn zero() -> Self {
- OriginComponent::Length(LengthPercentage::Percentage(ComputedPercentage::zero()))
- }
-}
-
-/// A specified CSS `rotate`
-pub type Rotate = generic::Rotate<Number, Angle>;
-
-impl Parse for Rotate {
- fn parse<'i, 't>(
- context: &ParserContext,
- input: &mut Parser<'i, 't>,
- ) -> Result<Self, ParseError<'i>> {
- if input.try_parse(|i| i.expect_ident_matching("none")).is_ok() {
- return Ok(generic::Rotate::None);
- }
-
- // Parse <angle> or [ x | y | z | <number>{3} ] && <angle>.
- //
- // The rotate axis and angle could be in any order, so we parse angle twice to cover
- // two cases. i.e. `<number>{3} <angle>` or `<angle> <number>{3}`
- let angle = input
- .try_parse(|i| specified::Angle::parse(context, i))
- .ok();
- let axis = input
- .try_parse(|i| {
- Ok(try_match_ident_ignore_ascii_case! { i,
- "x" => (Number::new(1.), Number::new(0.), Number::new(0.)),
- "y" => (Number::new(0.), Number::new(1.), Number::new(0.)),
- "z" => (Number::new(0.), Number::new(0.), Number::new(1.)),
- })
- })
- .or_else(|_: ParseError| -> Result<_, ParseError> {
- input.try_parse(|i| {
- Ok((
- Number::parse(context, i)?,
- Number::parse(context, i)?,
- Number::parse(context, i)?,
- ))
- })
- })
- .ok();
- let angle = match angle {
- Some(a) => a,
- None => specified::Angle::parse(context, input)?,
- };
-
- Ok(match axis {
- Some((x, y, z)) => generic::Rotate::Rotate3D(x, y, z, angle),
- None => generic::Rotate::Rotate(angle),
- })
- }
-}
-
-/// A specified CSS `translate`
-pub type Translate = generic::Translate<LengthPercentage, Length>;
-
-impl Parse for Translate {
- fn parse<'i, 't>(
- context: &ParserContext,
- input: &mut Parser<'i, 't>,
- ) -> Result<Self, ParseError<'i>> {
- if input.try_parse(|i| i.expect_ident_matching("none")).is_ok() {
- return Ok(generic::Translate::None);
- }
-
- let tx = specified::LengthPercentage::parse(context, input)?;
- if let Ok(ty) = input.try_parse(|i| specified::LengthPercentage::parse(context, i)) {
- if let Ok(tz) = input.try_parse(|i| specified::Length::parse(context, i)) {
- // 'translate: <length-percentage> <length-percentage> <length>'
- return Ok(generic::Translate::Translate(tx, ty, tz));
- }
-
- // translate: <length-percentage> <length-percentage>'
- return Ok(generic::Translate::Translate(
- tx,
- ty,
- specified::Length::zero(),
- ));
- }
-
- // 'translate: <length-percentage> '
- Ok(generic::Translate::Translate(
- tx,
- specified::LengthPercentage::zero(),
- specified::Length::zero(),
- ))
- }
-}
-
-/// A specified CSS `scale`
-pub type Scale = generic::Scale<Number>;
-
-impl Parse for Scale {
- /// Scale accepts <number> | <percentage>, so we parse it as NumberOrPercentage,
- /// and then convert into an Number if it's a Percentage.
- /// https://github.com/w3c/csswg-drafts/pull/4396
- fn parse<'i, 't>(
- context: &ParserContext,
- input: &mut Parser<'i, 't>,
- ) -> Result<Self, ParseError<'i>> {
- if input.try_parse(|i| i.expect_ident_matching("none")).is_ok() {
- return Ok(generic::Scale::None);
- }
-
- let sx = NumberOrPercentage::parse(context, input)?.to_number();
- if let Ok(sy) = input.try_parse(|i| NumberOrPercentage::parse(context, i)) {
- let sy = sy.to_number();
- if let Ok(sz) = input.try_parse(|i| NumberOrPercentage::parse(context, i)) {
- // 'scale: <number> <number> <number>'
- return Ok(generic::Scale::Scale(sx, sy, sz.to_number()));
- }
-
- // 'scale: <number> <number>'
- return Ok(generic::Scale::Scale(sx, sy, Number::new(1.0)));
- }
-
- // 'scale: <number>'
- Ok(generic::Scale::Scale(sx, sx, Number::new(1.0)))
- }
-}
diff --git a/components/style/values/specified/ui.rs b/components/style/values/specified/ui.rs
deleted file mode 100644
index 0c656faab20..00000000000
--- a/components/style/values/specified/ui.rs
+++ /dev/null
@@ -1,232 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
-
-//! Specified types for UI properties.
-
-use crate::parser::{Parse, ParserContext};
-use crate::values::generics::ui as generics;
-use crate::values::specified::color::Color;
-use crate::values::specified::image::Image;
-use crate::values::specified::Number;
-use cssparser::Parser;
-use std::fmt::{self, Write};
-use style_traits::{
- CssWriter, KeywordsCollectFn, ParseError, SpecifiedValueInfo, StyleParseErrorKind, ToCss,
-};
-
-/// A specified value for the `cursor` property.
-pub type Cursor = generics::GenericCursor<CursorImage>;
-
-/// A specified value for item of `image cursors`.
-pub type CursorImage = generics::GenericCursorImage<Image, Number>;
-
-impl Parse for Cursor {
- /// cursor: [<url> [<number> <number>]?]# [auto | default | ...]
- fn parse<'i, 't>(
- context: &ParserContext,
- input: &mut Parser<'i, 't>,
- ) -> Result<Self, ParseError<'i>> {
- let mut images = vec![];
- loop {
- match input.try_parse(|input| CursorImage::parse(context, input)) {
- Ok(image) => images.push(image),
- Err(_) => break,
- }
- input.expect_comma()?;
- }
- Ok(Self {
- images: images.into(),
- keyword: CursorKind::parse(input)?,
- })
- }
-}
-
-impl Parse for CursorImage {
- fn parse<'i, 't>(
- context: &ParserContext,
- input: &mut Parser<'i, 't>,
- ) -> Result<Self, ParseError<'i>> {
- use crate::Zero;
-
- let image = Image::parse_only_url(context, input)?;
- let mut has_hotspot = false;
- let mut hotspot_x = Number::zero();
- let mut hotspot_y = Number::zero();
-
- if let Ok(x) = input.try_parse(|input| Number::parse(context, input)) {
- has_hotspot = true;
- hotspot_x = x;
- hotspot_y = Number::parse(context, input)?;
- }
-
- Ok(Self {
- image,
- has_hotspot,
- hotspot_x,
- hotspot_y,
- })
- }
-}
-
-// This trait is manually implemented because we don't support the whole <image>
-// syntax for cursors
-impl SpecifiedValueInfo for CursorImage {
- fn collect_completion_keywords(f: KeywordsCollectFn) {
- f(&["url", "image-set"]);
- }
-}
-/// Specified value of `-moz-force-broken-image-icon`
-#[derive(
- Clone,
- Copy,
- Debug,
- MallocSizeOf,
- PartialEq,
- SpecifiedValueInfo,
- ToComputedValue,
- ToResolvedValue,
- ToShmem,
-)]
-#[repr(transparent)]
-pub struct BoolInteger(pub bool);
-
-impl BoolInteger {
- /// Returns 0
- #[inline]
- pub fn zero() -> Self {
- Self(false)
- }
-}
-
-impl Parse for BoolInteger {
- fn parse<'i, 't>(
- _context: &ParserContext,
- input: &mut Parser<'i, 't>,
- ) -> Result<Self, ParseError<'i>> {
- // We intentionally don't support calc values here.
- match input.expect_integer()? {
- 0 => Ok(Self(false)),
- 1 => Ok(Self(true)),
- _ => Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError)),
- }
- }
-}
-
-impl ToCss for BoolInteger {
- fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
- where
- W: Write,
- {
- dest.write_str(if self.0 { "1" } else { "0" })
- }
-}
-
-/// A specified value for `scrollbar-color` property
-pub type ScrollbarColor = generics::ScrollbarColor<Color>;
-
-impl Parse for ScrollbarColor {
- fn parse<'i, 't>(
- context: &ParserContext,
- input: &mut Parser<'i, 't>,
- ) -> Result<Self, ParseError<'i>> {
- if input.try_parse(|i| i.expect_ident_matching("auto")).is_ok() {
- return Ok(generics::ScrollbarColor::Auto);
- }
- Ok(generics::ScrollbarColor::Colors {
- thumb: Color::parse(context, input)?,
- track: Color::parse(context, input)?,
- })
- }
-}
-
-/// The specified value for the `user-select` property.
-///
-/// https://drafts.csswg.org/css-ui-4/#propdef-user-select
-#[allow(missing_docs)]
-#[derive(
- Clone,
- Copy,
- Debug,
- Eq,
- MallocSizeOf,
- Parse,
- PartialEq,
- SpecifiedValueInfo,
- ToComputedValue,
- ToCss,
- ToResolvedValue,
- ToShmem,
-)]
-#[repr(u8)]
-pub enum UserSelect {
- Auto,
- Text,
- #[parse(aliases = "-moz-none")]
- None,
- /// Force selection of all children.
- All,
-}
-
-/// The keywords allowed in the Cursor property.
-///
-/// https://drafts.csswg.org/css-ui-4/#propdef-cursor
-#[allow(missing_docs)]
-#[derive(
- Clone,
- Copy,
- Debug,
- Eq,
- FromPrimitive,
- MallocSizeOf,
- Parse,
- PartialEq,
- SpecifiedValueInfo,
- ToComputedValue,
- ToCss,
- ToResolvedValue,
- ToShmem,
-)]
-#[repr(u8)]
-pub enum CursorKind {
- None,
- Default,
- Pointer,
- ContextMenu,
- Help,
- Progress,
- Wait,
- Cell,
- Crosshair,
- Text,
- VerticalText,
- Alias,
- Copy,
- Move,
- NoDrop,
- NotAllowed,
- #[parse(aliases = "-moz-grab")]
- Grab,
- #[parse(aliases = "-moz-grabbing")]
- Grabbing,
- EResize,
- NResize,
- NeResize,
- NwResize,
- SResize,
- SeResize,
- SwResize,
- WResize,
- EwResize,
- NsResize,
- NeswResize,
- NwseResize,
- ColResize,
- RowResize,
- AllScroll,
- #[parse(aliases = "-moz-zoom-in")]
- ZoomIn,
- #[parse(aliases = "-moz-zoom-out")]
- ZoomOut,
- Auto,
-}
diff --git a/components/style/values/specified/url.rs b/components/style/values/specified/url.rs
deleted file mode 100644
index 17ecbe0d5e8..00000000000
--- a/components/style/values/specified/url.rs
+++ /dev/null
@@ -1,15 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
-
-//! Common handling for the specified value CSS url() values.
-
-use crate::values::generics::url::GenericUrlOrNone;
-
-#[cfg(feature = "gecko")]
-pub use crate::gecko::url::{SpecifiedImageUrl, SpecifiedUrl};
-#[cfg(feature = "servo")]
-pub use crate::servo::url::{SpecifiedImageUrl, SpecifiedUrl};
-
-/// Specified <url> | <none>
-pub type UrlOrNone = GenericUrlOrNone<SpecifiedUrl>;
diff --git a/components/style_config/Cargo.toml b/components/style_config/Cargo.toml
deleted file mode 100644
index 02507dacf0c..00000000000
--- a/components/style_config/Cargo.toml
+++ /dev/null
@@ -1,14 +0,0 @@
-[package]
-name = "style_config"
-version = "0.0.1"
-authors = ["The Servo Project Developers"]
-license = "MPL-2.0"
-edition = "2021"
-publish = false
-
-[lib]
-name = "style_config"
-path = "lib.rs"
-
-[dependencies]
-lazy_static = { workspace = true }
diff --git a/components/style_config/lib.rs b/components/style_config/lib.rs
deleted file mode 100644
index 8b2c2fc4528..00000000000
--- a/components/style_config/lib.rs
+++ /dev/null
@@ -1,97 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
-
-use std::collections::HashMap;
-use std::sync::RwLock;
-
-use lazy_static::lazy_static;
-
-lazy_static! {
- static ref PREFS: Preferences = Preferences::default();
-}
-
-#[derive(Debug, Default)]
-pub struct Preferences {
- // When adding a new pref type, be sure to update the TryFrom<&PrefValue> in
- // servo_config, to plumb the values in from Servo.
- bool_prefs: RwLock<HashMap<String, bool>>,
- i32_prefs: RwLock<HashMap<String, i32>>,
-}
-
-impl Preferences {
- pub fn get_bool(&self, key: &str) -> bool {
- let prefs = self.bool_prefs.read().expect("RwLock is poisoned");
- *prefs.get(key).unwrap_or(&false)
- }
-
- pub fn get_i32(&self, key: &str) -> i32 {
- let prefs = self.i32_prefs.read().expect("RwLock is poisoned");
- *prefs.get(key).unwrap_or(&0)
- }
-
- pub fn set_bool(&self, key: &str, value: bool) {
- let mut prefs = self.bool_prefs.write().expect("RwLock is poisoned");
-
- // Avoid cloning the key if it exists.
- if let Some(pref) = prefs.get_mut(key) {
- *pref = value;
- } else {
- prefs.insert(key.to_owned(), value);
- }
- }
-
- pub fn set_i32(&self, key: &str, value: i32) {
- let mut prefs = self.i32_prefs.write().expect("RwLock is poisoned");
-
- // Avoid cloning the key if it exists.
- if let Some(pref) = prefs.get_mut(key) {
- *pref = value;
- } else {
- prefs.insert(key.to_owned(), value);
- }
- }
-}
-
-pub fn get_bool(key: &str) -> bool {
- PREFS.get_bool(key)
-}
-
-pub fn get_i32(key: &str) -> i32 {
- PREFS.get_i32(key)
-}
-
-pub fn set_bool(key: &str, value: bool) {
- PREFS.set_bool(key, value)
-}
-
-pub fn set_i32(key: &str, value: i32) {
- PREFS.set_i32(key, value)
-}
-
-#[test]
-fn test() {
- let mut prefs = Preferences::default();
-
- // Prefs have default values when unset.
- assert_eq!(prefs.get_bool("foo"), false);
- assert_eq!(prefs.get_i32("bar"), 0);
-
- // Prefs can be set and retrieved.
- prefs.set_bool("foo", true);
- prefs.set_i32("bar", 1);
- assert_eq!(prefs.get_bool("foo"), true);
- assert_eq!(prefs.get_i32("bar"), 1);
- prefs.set_bool("foo", false);
- prefs.set_i32("bar", 2);
- assert_eq!(prefs.get_bool("foo"), false);
- assert_eq!(prefs.get_i32("bar"), 2);
-
- // Each value type currently has an independent namespace.
- prefs.set_i32("foo", 3);
- prefs.set_bool("bar", true);
- assert_eq!(prefs.get_i32("foo"), 3);
- assert_eq!(prefs.get_bool("foo"), false);
- assert_eq!(prefs.get_bool("bar"), true);
- assert_eq!(prefs.get_i32("bar"), 2);
-}
diff --git a/components/style_derive/Cargo.toml b/components/style_derive/Cargo.toml
deleted file mode 100644
index 98dc1852e8c..00000000000
--- a/components/style_derive/Cargo.toml
+++ /dev/null
@@ -1,18 +0,0 @@
-[package]
-name = "style_derive"
-version = "0.0.1"
-authors = ["The Servo Project Developers"]
-license = "MPL-2.0"
-publish = false
-
-[lib]
-path = "lib.rs"
-proc-macro = true
-
-[dependencies]
-darling = { workspace = true, default-features = false }
-derive_common = { path = "../derive_common" }
-proc-macro2 = "1"
-quote = "1"
-syn = { workspace = true }
-synstructure = { workspace = true }
diff --git a/components/style_derive/animate.rs b/components/style_derive/animate.rs
deleted file mode 100644
index 9549100ad08..00000000000
--- a/components/style_derive/animate.rs
+++ /dev/null
@@ -1,135 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
-
-use darling::util::PathList;
-use derive_common::cg;
-use proc_macro2::TokenStream;
-use quote::TokenStreamExt;
-use syn::{DeriveInput, WhereClause};
-use synstructure::{Structure, VariantInfo};
-
-pub fn derive(mut input: DeriveInput) -> TokenStream {
- let animation_input_attrs = cg::parse_input_attrs::<AnimationInputAttrs>(&input);
-
- let no_bound = animation_input_attrs.no_bound.unwrap_or_default();
- let mut where_clause = input.generics.where_clause.take();
- for param in input.generics.type_params() {
- if !no_bound.iter().any(|name| name.is_ident(&param.ident)) {
- cg::add_predicate(
- &mut where_clause,
- parse_quote!(#param: crate::values::animated::Animate),
- );
- }
- }
- let (mut match_body, needs_catchall_branch) = {
- let s = Structure::new(&input);
- let needs_catchall_branch = s.variants().len() > 1;
- let match_body = s.variants().iter().fold(quote!(), |body, variant| {
- let arm = derive_variant_arm(variant, &mut where_clause);
- quote! { #body #arm }
- });
- (match_body, needs_catchall_branch)
- };
-
- input.generics.where_clause = where_clause;
-
- if needs_catchall_branch {
- // This ideally shouldn't be needed, but see
- // https://github.com/rust-lang/rust/issues/68867
- match_body.append_all(quote! { _ => unsafe { debug_unreachable!() } });
- }
-
- let name = &input.ident;
- let (impl_generics, ty_generics, where_clause) = input.generics.split_for_impl();
-
- quote! {
- impl #impl_generics crate::values::animated::Animate for #name #ty_generics #where_clause {
- #[allow(unused_variables, unused_imports)]
- #[inline]
- fn animate(
- &self,
- other: &Self,
- procedure: crate::values::animated::Procedure,
- ) -> Result<Self, ()> {
- if std::mem::discriminant(self) != std::mem::discriminant(other) {
- return Err(());
- }
- match (self, other) {
- #match_body
- }
- }
- }
- }
-}
-
-fn derive_variant_arm(
- variant: &VariantInfo,
- where_clause: &mut Option<WhereClause>,
-) -> TokenStream {
- let variant_attrs = cg::parse_variant_attrs_from_ast::<AnimationVariantAttrs>(&variant.ast());
- let (this_pattern, this_info) = cg::ref_pattern(&variant, "this");
- let (other_pattern, other_info) = cg::ref_pattern(&variant, "other");
-
- if variant_attrs.error {
- return quote! {
- (&#this_pattern, &#other_pattern) => Err(()),
- };
- }
-
- let (result_value, result_info) = cg::value(&variant, "result");
- let mut computations = quote!();
- let iter = result_info.iter().zip(this_info.iter().zip(&other_info));
- computations.append_all(iter.map(|(result, (this, other))| {
- let field_attrs = cg::parse_field_attrs::<AnimationFieldAttrs>(&result.ast());
- if field_attrs.field_bound {
- let ty = &this.ast().ty;
- cg::add_predicate(
- where_clause,
- parse_quote!(#ty: crate::values::animated::Animate),
- );
- }
- if field_attrs.constant {
- quote! {
- if #this != #other {
- return Err(());
- }
- let #result = std::clone::Clone::clone(#this);
- }
- } else {
- quote! {
- let #result =
- crate::values::animated::Animate::animate(#this, #other, procedure)?;
- }
- }
- }));
-
- quote! {
- (&#this_pattern, &#other_pattern) => {
- #computations
- Ok(#result_value)
- }
- }
-}
-
-#[derive(Default, FromDeriveInput)]
-#[darling(attributes(animation), default)]
-pub struct AnimationInputAttrs {
- pub no_bound: Option<PathList>,
-}
-
-#[derive(Default, FromVariant)]
-#[darling(attributes(animation), default)]
-pub struct AnimationVariantAttrs {
- pub error: bool,
- // Only here because of structs, where the struct definition acts as a
- // variant itself.
- pub no_bound: Option<PathList>,
-}
-
-#[derive(Default, FromField)]
-#[darling(attributes(animation), default)]
-pub struct AnimationFieldAttrs {
- pub constant: bool,
- pub field_bound: bool,
-}
diff --git a/components/style_derive/compute_squared_distance.rs b/components/style_derive/compute_squared_distance.rs
deleted file mode 100644
index 022ab115eea..00000000000
--- a/components/style_derive/compute_squared_distance.rs
+++ /dev/null
@@ -1,125 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
-
-use crate::animate::{AnimationFieldAttrs, AnimationInputAttrs, AnimationVariantAttrs};
-use derive_common::cg;
-use proc_macro2::TokenStream;
-use quote::TokenStreamExt;
-use syn::{DeriveInput, WhereClause};
-use synstructure;
-
-pub fn derive(mut input: DeriveInput) -> TokenStream {
- let animation_input_attrs = cg::parse_input_attrs::<AnimationInputAttrs>(&input);
- let no_bound = animation_input_attrs.no_bound.unwrap_or_default();
- let mut where_clause = input.generics.where_clause.take();
- for param in input.generics.type_params() {
- if !no_bound.iter().any(|name| name.is_ident(&param.ident)) {
- cg::add_predicate(
- &mut where_clause,
- parse_quote!(#param: crate::values::distance::ComputeSquaredDistance),
- );
- }
- }
-
- let (mut match_body, needs_catchall_branch) = {
- let s = synstructure::Structure::new(&input);
- let needs_catchall_branch = s.variants().len() > 1;
-
- let match_body = s.variants().iter().fold(quote!(), |body, variant| {
- let arm = derive_variant_arm(variant, &mut where_clause);
- quote! { #body #arm }
- });
-
- (match_body, needs_catchall_branch)
- };
-
- input.generics.where_clause = where_clause;
-
- if needs_catchall_branch {
- // This ideally shouldn't be needed, but see:
- // https://github.com/rust-lang/rust/issues/68867
- match_body.append_all(quote! { _ => unsafe { debug_unreachable!() } });
- }
-
- let name = &input.ident;
- let (impl_generics, ty_generics, where_clause) = input.generics.split_for_impl();
-
- quote! {
- impl #impl_generics crate::values::distance::ComputeSquaredDistance for #name #ty_generics #where_clause {
- #[allow(unused_variables, unused_imports)]
- #[inline]
- fn compute_squared_distance(
- &self,
- other: &Self,
- ) -> Result<crate::values::distance::SquaredDistance, ()> {
- if std::mem::discriminant(self) != std::mem::discriminant(other) {
- return Err(());
- }
- match (self, other) {
- #match_body
- }
- }
- }
- }
-}
-
-fn derive_variant_arm(
- variant: &synstructure::VariantInfo,
- mut where_clause: &mut Option<WhereClause>,
-) -> TokenStream {
- let variant_attrs = cg::parse_variant_attrs_from_ast::<AnimationVariantAttrs>(&variant.ast());
- let (this_pattern, this_info) = cg::ref_pattern(&variant, "this");
- let (other_pattern, other_info) = cg::ref_pattern(&variant, "other");
-
- if variant_attrs.error {
- return quote! {
- (&#this_pattern, &#other_pattern) => Err(()),
- };
- }
-
- let sum = if this_info.is_empty() {
- quote! { crate::values::distance::SquaredDistance::from_sqrt(0.) }
- } else {
- let mut sum = quote!();
- sum.append_separated(this_info.iter().zip(&other_info).map(|(this, other)| {
- let field_attrs = cg::parse_field_attrs::<DistanceFieldAttrs>(&this.ast());
- if field_attrs.field_bound {
- let ty = &this.ast().ty;
- cg::add_predicate(
- &mut where_clause,
- parse_quote!(#ty: crate::values::distance::ComputeSquaredDistance),
- );
- }
-
- let animation_field_attrs =
- cg::parse_field_attrs::<AnimationFieldAttrs>(&this.ast());
-
- if animation_field_attrs.constant {
- quote! {
- {
- if #this != #other {
- return Err(());
- }
- crate::values::distance::SquaredDistance::from_sqrt(0.)
- }
- }
- } else {
- quote! {
- crate::values::distance::ComputeSquaredDistance::compute_squared_distance(#this, #other)?
- }
- }
- }), quote!(+));
- sum
- };
-
- return quote! {
- (&#this_pattern, &#other_pattern) => Ok(#sum),
- };
-}
-
-#[derive(Default, FromField)]
-#[darling(attributes(distance), default)]
-struct DistanceFieldAttrs {
- field_bound: bool,
-}
diff --git a/components/style_derive/lib.rs b/components/style_derive/lib.rs
deleted file mode 100644
index 079db00c5a3..00000000000
--- a/components/style_derive/lib.rs
+++ /dev/null
@@ -1,82 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
-
-#![recursion_limit = "128"]
-
-#[macro_use]
-extern crate darling;
-extern crate derive_common;
-extern crate proc_macro;
-extern crate proc_macro2;
-#[macro_use]
-extern crate quote;
-#[macro_use]
-extern crate syn;
-extern crate synstructure;
-
-use proc_macro::TokenStream;
-
-mod animate;
-mod compute_squared_distance;
-mod parse;
-mod specified_value_info;
-mod to_animated_value;
-mod to_animated_zero;
-mod to_computed_value;
-mod to_css;
-mod to_resolved_value;
-
-#[proc_macro_derive(Animate, attributes(animate, animation))]
-pub fn derive_animate(stream: TokenStream) -> TokenStream {
- let input = syn::parse(stream).unwrap();
- animate::derive(input).into()
-}
-
-#[proc_macro_derive(ComputeSquaredDistance, attributes(animation, distance))]
-pub fn derive_compute_squared_distance(stream: TokenStream) -> TokenStream {
- let input = syn::parse(stream).unwrap();
- compute_squared_distance::derive(input).into()
-}
-
-#[proc_macro_derive(ToAnimatedValue)]
-pub fn derive_to_animated_value(stream: TokenStream) -> TokenStream {
- let input = syn::parse(stream).unwrap();
- to_animated_value::derive(input).into()
-}
-
-#[proc_macro_derive(Parse, attributes(css, parse))]
-pub fn derive_parse(stream: TokenStream) -> TokenStream {
- let input = syn::parse(stream).unwrap();
- parse::derive(input).into()
-}
-
-#[proc_macro_derive(ToAnimatedZero, attributes(animation, zero))]
-pub fn derive_to_animated_zero(stream: TokenStream) -> TokenStream {
- let input = syn::parse(stream).unwrap();
- to_animated_zero::derive(input).into()
-}
-
-#[proc_macro_derive(ToComputedValue, attributes(compute))]
-pub fn derive_to_computed_value(stream: TokenStream) -> TokenStream {
- let input = syn::parse(stream).unwrap();
- to_computed_value::derive(input).into()
-}
-
-#[proc_macro_derive(ToResolvedValue, attributes(resolve))]
-pub fn derive_to_resolved_value(stream: TokenStream) -> TokenStream {
- let input = syn::parse(stream).unwrap();
- to_resolved_value::derive(input).into()
-}
-
-#[proc_macro_derive(ToCss, attributes(css))]
-pub fn derive_to_css(stream: TokenStream) -> TokenStream {
- let input = syn::parse(stream).unwrap();
- to_css::derive(input).into()
-}
-
-#[proc_macro_derive(SpecifiedValueInfo, attributes(css, parse, value_info))]
-pub fn derive_specified_value_info(stream: TokenStream) -> TokenStream {
- let input = syn::parse(stream).unwrap();
- specified_value_info::derive(input).into()
-}
diff --git a/components/style_derive/parse.rs b/components/style_derive/parse.rs
deleted file mode 100644
index b1a1213435c..00000000000
--- a/components/style_derive/parse.rs
+++ /dev/null
@@ -1,323 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
-
-use crate::to_css::{CssBitflagAttrs, CssVariantAttrs};
-use derive_common::cg;
-use proc_macro2::{Span, TokenStream};
-use quote::TokenStreamExt;
-use syn::{self, DeriveInput, Ident, Path};
-use synstructure::{Structure, VariantInfo};
-
-#[derive(Default, FromVariant)]
-#[darling(attributes(parse), default)]
-pub struct ParseVariantAttrs {
- pub aliases: Option<String>,
- pub condition: Option<Path>,
-}
-
-#[derive(Default, FromField)]
-#[darling(attributes(parse), default)]
-pub struct ParseFieldAttrs {
- field_bound: bool,
-}
-
-fn parse_bitflags(bitflags: &CssBitflagAttrs) -> TokenStream {
- let mut match_arms = TokenStream::new();
- for (rust_name, css_name) in bitflags.single_flags() {
- let rust_ident = Ident::new(&rust_name, Span::call_site());
- match_arms.append_all(quote! {
- #css_name if result.is_empty() => {
- single_flag = true;
- Self::#rust_ident
- },
- });
- }
-
- for (rust_name, css_name) in bitflags.mixed_flags() {
- let rust_ident = Ident::new(&rust_name, Span::call_site());
- match_arms.append_all(quote! {
- #css_name => Self::#rust_ident,
- });
- }
-
- let mut validate_condition = quote! { !result.is_empty() };
- if let Some(ref function) = bitflags.validate_mixed {
- validate_condition.append_all(quote! {
- && #function(&mut result)
- });
- }
-
- // NOTE(emilio): this loop has this weird structure because we run this code
- // to parse stuff like text-decoration-line in the text-decoration
- // shorthand, so we need to be a bit careful that we don't error if we don't
- // consume the whole thing because we find an invalid identifier or other
- // kind of token. Instead, we should leave it unconsumed.
- quote! {
- let mut result = Self::empty();
- loop {
- let mut single_flag = false;
- let flag: Result<_, style_traits::ParseError<'i>> = input.try_parse(|input| {
- Ok(try_match_ident_ignore_ascii_case! { input,
- #match_arms
- })
- });
-
- let flag = match flag {
- Ok(flag) => flag,
- Err(..) => break,
- };
-
- if single_flag {
- return Ok(flag);
- }
-
- if result.intersects(flag) {
- return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError));
- }
-
- result.insert(flag);
- }
- if #validate_condition {
- Ok(result)
- } else {
- Err(input.new_custom_error(style_traits::StyleParseErrorKind::UnspecifiedError))
- }
- }
-}
-
-fn parse_non_keyword_variant(
- where_clause: &mut Option<syn::WhereClause>,
- name: &syn::Ident,
- variant: &VariantInfo,
- variant_attrs: &CssVariantAttrs,
- parse_attrs: &ParseVariantAttrs,
- skip_try: bool,
-) -> TokenStream {
- let bindings = variant.bindings();
- assert!(parse_attrs.aliases.is_none());
- assert!(variant_attrs.function.is_none());
- assert!(variant_attrs.keyword.is_none());
- assert_eq!(
- bindings.len(),
- 1,
- "We only support deriving parse for simple variants"
- );
- let variant_name = &variant.ast().ident;
- let binding_ast = &bindings[0].ast();
- let ty = &binding_ast.ty;
-
- if let Some(ref bitflags) = variant_attrs.bitflags {
- assert!(skip_try, "Should be the only variant");
- assert!(
- parse_attrs.condition.is_none(),
- "Should be the only variant"
- );
- assert!(where_clause.is_none(), "Generic bitflags?");
- return parse_bitflags(bitflags);
- }
-
- let field_attrs = cg::parse_field_attrs::<ParseFieldAttrs>(binding_ast);
- if field_attrs.field_bound {
- cg::add_predicate(where_clause, parse_quote!(#ty: crate::parser::Parse));
- }
-
- let mut parse = if skip_try {
- quote! {
- let v = <#ty as crate::parser::Parse>::parse(context, input)?;
- return Ok(#name::#variant_name(v));
- }
- } else {
- quote! {
- if let Ok(v) = input.try(|i| <#ty as crate::parser::Parse>::parse(context, i)) {
- return Ok(#name::#variant_name(v));
- }
- }
- };
-
- if let Some(ref condition) = parse_attrs.condition {
- parse = quote! {
- if #condition(context) {
- #parse
- }
- };
-
- if skip_try {
- // We're the last variant and we can fail to parse due to the
- // condition clause. If that happens, we need to return an error.
- parse = quote! {
- #parse
- Err(input.new_custom_error(style_traits::StyleParseErrorKind::UnspecifiedError))
- };
- }
- }
-
- parse
-}
-
-pub fn derive(mut input: DeriveInput) -> TokenStream {
- let mut where_clause = input.generics.where_clause.take();
- for param in input.generics.type_params() {
- cg::add_predicate(
- &mut where_clause,
- parse_quote!(#param: crate::parser::Parse),
- );
- }
-
- let name = &input.ident;
- let s = Structure::new(&input);
-
- let mut saw_condition = false;
- let mut match_keywords = quote! {};
- let mut non_keywords = vec![];
-
- let mut effective_variants = 0;
- for variant in s.variants().iter() {
- let css_variant_attrs = cg::parse_variant_attrs_from_ast::<CssVariantAttrs>(&variant.ast());
- if css_variant_attrs.skip {
- continue;
- }
- effective_variants += 1;
-
- let parse_attrs = cg::parse_variant_attrs_from_ast::<ParseVariantAttrs>(&variant.ast());
-
- saw_condition |= parse_attrs.condition.is_some();
-
- if !variant.bindings().is_empty() {
- non_keywords.push((variant, css_variant_attrs, parse_attrs));
- continue;
- }
-
- let identifier = cg::to_css_identifier(
- &css_variant_attrs
- .keyword
- .unwrap_or_else(|| variant.ast().ident.to_string()),
- );
- let ident = &variant.ast().ident;
-
- let condition = match parse_attrs.condition {
- Some(ref p) => quote! { if #p(context) },
- None => quote! {},
- };
-
- match_keywords.extend(quote! {
- #identifier #condition => Ok(#name::#ident),
- });
-
- let aliases = match parse_attrs.aliases {
- Some(aliases) => aliases,
- None => continue,
- };
-
- for alias in aliases.split(',') {
- match_keywords.extend(quote! {
- #alias #condition => Ok(#name::#ident),
- });
- }
- }
-
- let needs_context = saw_condition || !non_keywords.is_empty();
-
- let context_ident = if needs_context {
- quote! { context }
- } else {
- quote! { _ }
- };
-
- let has_keywords = non_keywords.len() != effective_variants;
-
- let mut parse_non_keywords = quote! {};
- for (i, (variant, css_attrs, parse_attrs)) in non_keywords.iter().enumerate() {
- let skip_try = !has_keywords && i == non_keywords.len() - 1;
- let parse_variant = parse_non_keyword_variant(
- &mut where_clause,
- name,
- variant,
- css_attrs,
- parse_attrs,
- skip_try,
- );
- parse_non_keywords.extend(parse_variant);
- }
-
- let parse_body = if needs_context {
- let parse_keywords = if has_keywords {
- quote! {
- let location = input.current_source_location();
- let ident = input.expect_ident()?;
- match_ignore_ascii_case! { &ident,
- #match_keywords
- _ => Err(location.new_unexpected_token_error(
- cssparser::Token::Ident(ident.clone())
- ))
- }
- }
- } else {
- quote! {}
- };
-
- quote! {
- #parse_non_keywords
- #parse_keywords
- }
- } else {
- quote! { Self::parse(input) }
- };
-
- let has_non_keywords = !non_keywords.is_empty();
-
- input.generics.where_clause = where_clause;
- let (impl_generics, ty_generics, where_clause) = input.generics.split_for_impl();
-
- let parse_trait_impl = quote! {
- impl #impl_generics crate::parser::Parse for #name #ty_generics #where_clause {
- #[inline]
- fn parse<'i, 't>(
- #context_ident: &crate::parser::ParserContext,
- input: &mut cssparser::Parser<'i, 't>,
- ) -> Result<Self, style_traits::ParseError<'i>> {
- #parse_body
- }
- }
- };
-
- if needs_context {
- return parse_trait_impl;
- }
-
- assert!(!has_non_keywords);
-
- // TODO(emilio): It'd be nice to get rid of these, but that makes the
- // conversion harder...
- let methods_impl = quote! {
- impl #name {
- /// Parse this keyword.
- #[inline]
- pub fn parse<'i, 't>(
- input: &mut cssparser::Parser<'i, 't>,
- ) -> Result<Self, style_traits::ParseError<'i>> {
- let location = input.current_source_location();
- let ident = input.expect_ident()?;
- Self::from_ident(ident.as_ref()).map_err(|()| {
- location.new_unexpected_token_error(
- cssparser::Token::Ident(ident.clone())
- )
- })
- }
-
- /// Parse this keyword from a string slice.
- #[inline]
- pub fn from_ident(ident: &str) -> Result<Self, ()> {
- match_ignore_ascii_case! { ident,
- #match_keywords
- _ => Err(()),
- }
- }
- }
- };
-
- quote! {
- #parse_trait_impl
- #methods_impl
- }
-}
diff --git a/components/style_derive/rustfmt.toml b/components/style_derive/rustfmt.toml
deleted file mode 100644
index c7ad93bafe3..00000000000
--- a/components/style_derive/rustfmt.toml
+++ /dev/null
@@ -1 +0,0 @@
-disable_all_formatting = true
diff --git a/components/style_derive/specified_value_info.rs b/components/style_derive/specified_value_info.rs
deleted file mode 100644
index e29f2bc416e..00000000000
--- a/components/style_derive/specified_value_info.rs
+++ /dev/null
@@ -1,195 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
-
-use crate::parse::ParseVariantAttrs;
-use crate::to_css::{CssFieldAttrs, CssInputAttrs, CssVariantAttrs};
-use derive_common::cg;
-use proc_macro2::TokenStream;
-use quote::TokenStreamExt;
-use syn::{Data, DeriveInput, Fields, Ident, Type};
-
-pub fn derive(mut input: DeriveInput) -> TokenStream {
- let css_attrs = cg::parse_input_attrs::<CssInputAttrs>(&input);
- let mut types = vec![];
- let mut values = vec![];
-
- let input_ident = &input.ident;
- let input_name = || cg::to_css_identifier(&input_ident.to_string());
- if let Some(function) = css_attrs.function {
- values.push(function.explicit().unwrap_or_else(input_name));
- // If the whole value is wrapped in a function, value types of
- // its fields should not be propagated.
- } else {
- let mut where_clause = input.generics.where_clause.take();
- for param in input.generics.type_params() {
- cg::add_predicate(
- &mut where_clause,
- parse_quote!(#param: style_traits::SpecifiedValueInfo),
- );
- }
- input.generics.where_clause = where_clause;
-
- match input.data {
- Data::Enum(ref e) => {
- for v in e.variants.iter() {
- let css_attrs = cg::parse_variant_attrs::<CssVariantAttrs>(&v);
- let info_attrs = cg::parse_variant_attrs::<ValueInfoVariantAttrs>(&v);
- let parse_attrs = cg::parse_variant_attrs::<ParseVariantAttrs>(&v);
- if css_attrs.skip {
- continue;
- }
- if let Some(aliases) = parse_attrs.aliases {
- for alias in aliases.split(',') {
- values.push(alias.to_string());
- }
- }
- if let Some(other_values) = info_attrs.other_values {
- for value in other_values.split(',') {
- values.push(value.to_string());
- }
- }
- let ident = &v.ident;
- let variant_name = || cg::to_css_identifier(&ident.to_string());
- if info_attrs.starts_with_keyword {
- values.push(variant_name());
- continue;
- }
- if let Some(keyword) = css_attrs.keyword {
- values.push(keyword);
- continue;
- }
- if let Some(function) = css_attrs.function {
- values.push(function.explicit().unwrap_or_else(variant_name));
- } else if !derive_struct_fields(&v.fields, &mut types, &mut values) {
- values.push(variant_name());
- }
- }
- },
- Data::Struct(ref s) => {
- if let Some(ref bitflags) = css_attrs.bitflags {
- for (_rust_name, css_name) in bitflags.single_flags() {
- values.push(css_name)
- }
- for (_rust_name, css_name) in bitflags.mixed_flags() {
- values.push(css_name)
- }
- } else if !derive_struct_fields(&s.fields, &mut types, &mut values) {
- values.push(input_name());
- }
- },
- Data::Union(_) => unreachable!("union is not supported"),
- }
- }
-
- let info_attrs = cg::parse_input_attrs::<ValueInfoInputAttrs>(&input);
- if let Some(other_values) = info_attrs.other_values {
- for value in other_values.split(',') {
- values.push(value.to_string());
- }
- }
-
- let mut types_value = quote!(0);
- types_value.append_all(types.iter().map(|ty| {
- quote! {
- | <#ty as style_traits::SpecifiedValueInfo>::SUPPORTED_TYPES
- }
- }));
-
- let mut nested_collects = quote!();
- nested_collects.append_all(types.iter().map(|ty| {
- quote! {
- <#ty as style_traits::SpecifiedValueInfo>::collect_completion_keywords(_f);
- }
- }));
-
- if let Some(ty) = info_attrs.ty {
- types_value.append_all(quote! {
- | style_traits::CssType::#ty
- });
- }
-
- let append_values = if values.is_empty() {
- quote!()
- } else {
- let mut value_list = quote!();
- value_list.append_separated(values.iter(), quote! { , });
- quote! { _f(&[#value_list]); }
- };
-
- let name = &input.ident;
- let (impl_generics, ty_generics, where_clause) = input.generics.split_for_impl();
-
- quote! {
- impl #impl_generics style_traits::SpecifiedValueInfo for #name #ty_generics
- #where_clause
- {
- const SUPPORTED_TYPES: u8 = #types_value;
-
- fn collect_completion_keywords(_f: &mut FnMut(&[&'static str])) {
- #nested_collects
- #append_values
- }
- }
- }
-}
-
-/// Derive from the given fields. Return false if the fields is a Unit,
-/// true otherwise.
-fn derive_struct_fields<'a>(
- fields: &'a Fields,
- types: &mut Vec<&'a Type>,
- values: &mut Vec<String>,
-) -> bool {
- let fields = match *fields {
- Fields::Unit => return false,
- Fields::Named(ref fields) => fields.named.iter(),
- Fields::Unnamed(ref fields) => fields.unnamed.iter(),
- };
- types.extend(fields.filter_map(|field| {
- let info_attrs = cg::parse_field_attrs::<ValueInfoFieldAttrs>(field);
- if let Some(other_values) = info_attrs.other_values {
- for value in other_values.split(',') {
- values.push(value.to_string());
- }
- }
- let css_attrs = cg::parse_field_attrs::<CssFieldAttrs>(field);
- if css_attrs.represents_keyword {
- let ident = field
- .ident
- .as_ref()
- .expect("only named field should use represents_keyword");
- values.push(cg::to_css_identifier(&ident.to_string()));
- return None;
- }
- if let Some(if_empty) = css_attrs.if_empty {
- values.push(if_empty);
- }
- if !css_attrs.skip {
- Some(&field.ty)
- } else {
- None
- }
- }));
- true
-}
-
-#[derive(Default, FromDeriveInput)]
-#[darling(attributes(value_info), default)]
-struct ValueInfoInputAttrs {
- ty: Option<Ident>,
- other_values: Option<String>,
-}
-
-#[derive(Default, FromVariant)]
-#[darling(attributes(value_info), default)]
-struct ValueInfoVariantAttrs {
- starts_with_keyword: bool,
- other_values: Option<String>,
-}
-
-#[derive(Default, FromField)]
-#[darling(attributes(value_info), default)]
-struct ValueInfoFieldAttrs {
- other_values: Option<String>,
-}
diff --git a/components/style_derive/to_animated_value.rs b/components/style_derive/to_animated_value.rs
deleted file mode 100644
index 45282f0c448..00000000000
--- a/components/style_derive/to_animated_value.rs
+++ /dev/null
@@ -1,35 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
-
-use proc_macro2::TokenStream;
-use syn::DeriveInput;
-use synstructure::BindStyle;
-use to_computed_value;
-
-pub fn derive(input: DeriveInput) -> TokenStream {
- let trait_impl = |from_body, to_body| {
- quote! {
- #[inline]
- fn from_animated_value(from: Self::AnimatedValue) -> Self {
- #from_body
- }
-
- #[inline]
- fn to_animated_value(self) -> Self::AnimatedValue {
- #to_body
- }
- }
- };
-
- to_computed_value::derive_to_value(
- input,
- parse_quote!(crate::values::animated::ToAnimatedValue),
- parse_quote!(AnimatedValue),
- BindStyle::Move,
- |_| Default::default(),
- |binding| quote!(crate::values::animated::ToAnimatedValue::from_animated_value(#binding)),
- |binding| quote!(crate::values::animated::ToAnimatedValue::to_animated_value(#binding)),
- trait_impl,
- )
-}
diff --git a/components/style_derive/to_animated_zero.rs b/components/style_derive/to_animated_zero.rs
deleted file mode 100644
index 008e94cbcfb..00000000000
--- a/components/style_derive/to_animated_zero.rs
+++ /dev/null
@@ -1,65 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
-
-use crate::animate::{AnimationFieldAttrs, AnimationInputAttrs, AnimationVariantAttrs};
-use derive_common::cg;
-use proc_macro2::TokenStream;
-use quote::TokenStreamExt;
-use syn;
-use synstructure;
-
-pub fn derive(mut input: syn::DeriveInput) -> TokenStream {
- let animation_input_attrs = cg::parse_input_attrs::<AnimationInputAttrs>(&input);
- let no_bound = animation_input_attrs.no_bound.unwrap_or_default();
- let mut where_clause = input.generics.where_clause.take();
- for param in input.generics.type_params() {
- if !no_bound.iter().any(|name| name.is_ident(&param.ident)) {
- cg::add_predicate(
- &mut where_clause,
- parse_quote!(#param: crate::values::animated::ToAnimatedZero),
- );
- }
- }
-
- let to_body = synstructure::Structure::new(&input).each_variant(|variant| {
- let attrs = cg::parse_variant_attrs_from_ast::<AnimationVariantAttrs>(&variant.ast());
- if attrs.error {
- return Some(quote! { Err(()) });
- }
- let (mapped, mapped_bindings) = cg::value(variant, "mapped");
- let bindings_pairs = variant.bindings().iter().zip(mapped_bindings);
- let mut computations = quote!();
- computations.append_all(bindings_pairs.map(|(binding, mapped_binding)| {
- let field_attrs = cg::parse_field_attrs::<AnimationFieldAttrs>(&binding.ast());
- if field_attrs.constant {
- quote! {
- let #mapped_binding = std::clone::Clone::clone(#binding);
- }
- } else {
- quote! {
- let #mapped_binding =
- crate::values::animated::ToAnimatedZero::to_animated_zero(#binding)?;
- }
- }
- }));
- computations.append_all(quote! { Ok(#mapped) });
- Some(computations)
- });
- input.generics.where_clause = where_clause;
-
- let name = &input.ident;
- let (impl_generics, ty_generics, where_clause) = input.generics.split_for_impl();
-
- quote! {
- impl #impl_generics crate::values::animated::ToAnimatedZero for #name #ty_generics #where_clause {
- #[allow(unused_variables)]
- #[inline]
- fn to_animated_zero(&self) -> Result<Self, ()> {
- match *self {
- #to_body
- }
- }
- }
- }
-}
diff --git a/components/style_derive/to_computed_value.rs b/components/style_derive/to_computed_value.rs
deleted file mode 100644
index 5e0f595c6b9..00000000000
--- a/components/style_derive/to_computed_value.rs
+++ /dev/null
@@ -1,205 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
-
-use derive_common::cg;
-use proc_macro2::TokenStream;
-use syn::{DeriveInput, Ident, Path};
-use synstructure::{BindStyle, BindingInfo};
-
-pub fn derive_to_value(
- mut input: DeriveInput,
- trait_path: Path,
- output_type_name: Ident,
- bind_style: BindStyle,
- // Returns whether to apply the field bound for a given item.
- mut binding_attrs: impl FnMut(&BindingInfo) -> ToValueAttrs,
- // Returns a token stream of the form: trait_path::from_foo(#binding)
- mut call_from: impl FnMut(&BindingInfo) -> TokenStream,
- mut call_to: impl FnMut(&BindingInfo) -> TokenStream,
- // Returns a tokenstream of the form:
- // fn from_function_syntax(foobar) -> Baz {
- // #first_arg
- // }
- //
- // fn to_function_syntax(foobar) -> Baz {
- // #second_arg
- // }
- mut trait_impl: impl FnMut(TokenStream, TokenStream) -> TokenStream,
-) -> TokenStream {
- let name = &input.ident;
-
- let mut where_clause = input.generics.where_clause.take();
- cg::propagate_clauses_to_output_type(
- &mut where_clause,
- &input.generics,
- &trait_path,
- &output_type_name,
- );
-
- let moves = match bind_style {
- BindStyle::Move | BindStyle::MoveMut => true,
- BindStyle::Ref | BindStyle::RefMut => false,
- };
-
- let params = input.generics.type_params().collect::<Vec<_>>();
- for param in &params {
- cg::add_predicate(&mut where_clause, parse_quote!(#param: #trait_path));
- }
-
- let computed_value_type = cg::fmap_trait_output(&input, &trait_path, &output_type_name);
-
- let mut add_field_bound = |binding: &BindingInfo| {
- let ty = &binding.ast().ty;
-
- let output_type = cg::map_type_params(
- ty,
- &params,
- &computed_value_type,
- &mut |ident| parse_quote!(<#ident as #trait_path>::#output_type_name),
- );
-
- cg::add_predicate(
- &mut where_clause,
- parse_quote!(
- #ty: #trait_path<#output_type_name = #output_type>
- ),
- );
- };
-
- let (to_body, from_body) = if params.is_empty() {
- let mut s = synstructure::Structure::new(&input);
- s.variants_mut().iter_mut().for_each(|v| {
- v.bind_with(|_| bind_style);
- });
-
- for variant in s.variants() {
- for binding in variant.bindings() {
- let attrs = binding_attrs(&binding);
- assert!(
- !attrs.field_bound,
- "It is default on a non-generic implementation",
- );
- if !attrs.no_field_bound {
- // Add field bounds to all bindings except the manually
- // excluded. This ensures the correctness of the clone() /
- // move based implementation.
- add_field_bound(binding);
- }
- }
- }
-
- let to_body = if moves {
- quote! { self }
- } else {
- quote! { std::clone::Clone::clone(self) }
- };
-
- let from_body = if moves {
- quote! { from }
- } else {
- quote! { std::clone::Clone::clone(from) }
- };
-
- (to_body, from_body)
- } else {
- let to_body = cg::fmap_match(&input, bind_style, |binding| {
- let attrs = binding_attrs(&binding);
- assert!(
- !attrs.no_field_bound,
- "It doesn't make sense on a generic implementation"
- );
- if attrs.field_bound {
- add_field_bound(&binding);
- }
- call_to(&binding)
- });
-
- let from_body = cg::fmap_match(&input, bind_style, |binding| call_from(&binding));
-
- let self_ = if moves {
- quote! { self }
- } else {
- quote! { *self }
- };
- let from_ = if moves {
- quote! { from }
- } else {
- quote! { *from }
- };
-
- let to_body = quote! {
- match #self_ {
- #to_body
- }
- };
-
- let from_body = quote! {
- match #from_ {
- #from_body
- }
- };
-
- (to_body, from_body)
- };
-
- input.generics.where_clause = where_clause;
- let (impl_generics, ty_generics, where_clause) = input.generics.split_for_impl();
-
- let impl_ = trait_impl(from_body, to_body);
-
- quote! {
- impl #impl_generics #trait_path for #name #ty_generics #where_clause {
- type #output_type_name = #computed_value_type;
-
- #impl_
- }
- }
-}
-
-pub fn derive(input: DeriveInput) -> TokenStream {
- let trait_impl = |from_body, to_body| {
- quote! {
- #[inline]
- fn from_computed_value(from: &Self::ComputedValue) -> Self {
- #from_body
- }
-
- #[allow(unused_variables)]
- #[inline]
- fn to_computed_value(&self, context: &crate::values::computed::Context) -> Self::ComputedValue {
- #to_body
- }
- }
- };
-
- derive_to_value(
- input,
- parse_quote!(crate::values::computed::ToComputedValue),
- parse_quote!(ComputedValue),
- BindStyle::Ref,
- |binding| {
- let attrs = cg::parse_field_attrs::<ComputedValueAttrs>(&binding.ast());
- ToValueAttrs {
- field_bound: attrs.field_bound,
- no_field_bound: attrs.no_field_bound,
- }
- },
- |binding| quote!(crate::values::computed::ToComputedValue::from_computed_value(#binding)),
- |binding| quote!(crate::values::computed::ToComputedValue::to_computed_value(#binding, context)),
- trait_impl,
- )
-}
-
-#[derive(Default)]
-pub struct ToValueAttrs {
- pub field_bound: bool,
- pub no_field_bound: bool,
-}
-
-#[derive(Default, FromField)]
-#[darling(attributes(compute), default)]
-struct ComputedValueAttrs {
- field_bound: bool,
- no_field_bound: bool,
-}
diff --git a/components/style_derive/to_css.rs b/components/style_derive/to_css.rs
deleted file mode 100644
index aa335366485..00000000000
--- a/components/style_derive/to_css.rs
+++ /dev/null
@@ -1,396 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
-
-use darling::util::Override;
-use derive_common::cg;
-use proc_macro2::{Span, TokenStream};
-use quote::{ToTokens, TokenStreamExt};
-use syn::{self, Data, Ident, Path, WhereClause};
-use synstructure::{BindingInfo, Structure, VariantInfo};
-
-fn derive_bitflags(input: &syn::DeriveInput, bitflags: &CssBitflagAttrs) -> TokenStream {
- let name = &input.ident;
- let mut body = TokenStream::new();
- for (rust_name, css_name) in bitflags.single_flags() {
- let rust_ident = Ident::new(&rust_name, Span::call_site());
- body.append_all(quote! {
- if *self == Self::#rust_ident {
- return dest.write_str(#css_name);
- }
- });
- }
-
- body.append_all(quote! {
- let mut has_any = false;
- });
-
- if bitflags.overlapping_bits {
- body.append_all(quote! {
- let mut serialized = Self::empty();
- });
- }
-
- for (rust_name, css_name) in bitflags.mixed_flags() {
- let rust_ident = Ident::new(&rust_name, Span::call_site());
- let serialize = quote! {
- if has_any {
- dest.write_char(' ')?;
- }
- has_any = true;
- dest.write_str(#css_name)?;
- };
- if bitflags.overlapping_bits {
- body.append_all(quote! {
- if self.contains(Self::#rust_ident) && !serialized.intersects(Self::#rust_ident) {
- #serialize
- serialized.insert(Self::#rust_ident);
- }
- });
- } else {
- body.append_all(quote! {
- if self.intersects(Self::#rust_ident) {
- #serialize
- }
- });
- }
- }
-
- body.append_all(quote! {
- Ok(())
- });
-
- quote! {
- impl style_traits::ToCss for #name {
- #[allow(unused_variables)]
- #[inline]
- fn to_css<W>(
- &self,
- dest: &mut style_traits::CssWriter<W>,
- ) -> std::fmt::Result
- where
- W: std::fmt::Write,
- {
- #body
- }
- }
- }
-}
-
-pub fn derive(mut input: syn::DeriveInput) -> TokenStream {
- let mut where_clause = input.generics.where_clause.take();
- for param in input.generics.type_params() {
- cg::add_predicate(&mut where_clause, parse_quote!(#param: style_traits::ToCss));
- }
-
- let input_attrs = cg::parse_input_attrs::<CssInputAttrs>(&input);
- if matches!(input.data, Data::Enum(..)) || input_attrs.bitflags.is_some() {
- assert!(
- input_attrs.function.is_none(),
- "#[css(function)] is not allowed on enums or bitflags"
- );
- assert!(
- !input_attrs.comma,
- "#[css(comma)] is not allowed on enums or bitflags"
- );
- }
-
- if let Some(ref bitflags) = input_attrs.bitflags {
- assert!(
- !input_attrs.derive_debug,
- "Bitflags can derive debug on their own"
- );
- assert!(where_clause.is_none(), "Generic bitflags?");
- return derive_bitflags(&input, bitflags);
- }
-
- let match_body = {
- let s = Structure::new(&input);
- s.each_variant(|variant| derive_variant_arm(variant, &mut where_clause))
- };
- input.generics.where_clause = where_clause;
-
- let name = &input.ident;
- let (impl_generics, ty_generics, where_clause) = input.generics.split_for_impl();
-
- let mut impls = quote! {
- impl #impl_generics style_traits::ToCss for #name #ty_generics #where_clause {
- #[allow(unused_variables)]
- #[inline]
- fn to_css<W>(
- &self,
- dest: &mut style_traits::CssWriter<W>,
- ) -> std::fmt::Result
- where
- W: std::fmt::Write,
- {
- match *self {
- #match_body
- }
- }
- }
- };
-
- if input_attrs.derive_debug {
- impls.append_all(quote! {
- impl #impl_generics std::fmt::Debug for #name #ty_generics #where_clause {
- fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
- style_traits::ToCss::to_css(
- self,
- &mut style_traits::CssWriter::new(f),
- )
- }
- }
- });
- }
-
- impls
-}
-
-fn derive_variant_arm(variant: &VariantInfo, generics: &mut Option<WhereClause>) -> TokenStream {
- let bindings = variant.bindings();
- let identifier = cg::to_css_identifier(&variant.ast().ident.to_string());
- let ast = variant.ast();
- let variant_attrs = cg::parse_variant_attrs_from_ast::<CssVariantAttrs>(&ast);
- let separator = if variant_attrs.comma { ", " } else { " " };
-
- if variant_attrs.skip {
- return quote!(Ok(()));
- }
- if variant_attrs.dimension {
- assert_eq!(bindings.len(), 1);
- assert!(
- variant_attrs.function.is_none() && variant_attrs.keyword.is_none(),
- "That makes no sense"
- );
- }
-
- let mut expr = if let Some(keyword) = variant_attrs.keyword {
- assert!(bindings.is_empty());
- quote! {
- std::fmt::Write::write_str(dest, #keyword)
- }
- } else if !bindings.is_empty() {
- derive_variant_fields_expr(bindings, generics, separator)
- } else {
- quote! {
- std::fmt::Write::write_str(dest, #identifier)
- }
- };
-
- if variant_attrs.dimension {
- expr = quote! {
- #expr?;
- std::fmt::Write::write_str(dest, #identifier)
- }
- } else if let Some(function) = variant_attrs.function {
- let mut identifier = function.explicit().map_or(identifier, |name| name);
- identifier.push('(');
- expr = quote! {
- std::fmt::Write::write_str(dest, #identifier)?;
- #expr?;
- std::fmt::Write::write_str(dest, ")")
- }
- }
- expr
-}
-
-fn derive_variant_fields_expr(
- bindings: &[BindingInfo],
- where_clause: &mut Option<WhereClause>,
- separator: &str,
-) -> TokenStream {
- let mut iter = bindings
- .iter()
- .filter_map(|binding| {
- let attrs = cg::parse_field_attrs::<CssFieldAttrs>(&binding.ast());
- if attrs.skip {
- return None;
- }
- Some((binding, attrs))
- })
- .peekable();
-
- let (first, attrs) = match iter.next() {
- Some(pair) => pair,
- None => return quote! { Ok(()) },
- };
- if attrs.field_bound {
- let ty = &first.ast().ty;
- // TODO(emilio): IntoIterator might not be enough for every type of
- // iterable thing (like ArcSlice<> or what not). We might want to expose
- // an `item = "T"` attribute to handle that in the future.
- let predicate = if attrs.iterable {
- parse_quote!(<#ty as IntoIterator>::Item: style_traits::ToCss)
- } else {
- parse_quote!(#ty: style_traits::ToCss)
- };
- cg::add_predicate(where_clause, predicate);
- }
- if !attrs.iterable && iter.peek().is_none() {
- let mut expr = quote! { style_traits::ToCss::to_css(#first, dest) };
- if let Some(condition) = attrs.skip_if {
- expr = quote! {
- if !#condition(#first) {
- #expr
- }
- }
- }
-
- if let Some(condition) = attrs.contextual_skip_if {
- expr = quote! {
- if !#condition(#(#bindings), *) {
- #expr
- }
- }
- }
- return expr;
- }
-
- let mut expr = derive_single_field_expr(first, attrs, where_clause, bindings);
- for (binding, attrs) in iter {
- derive_single_field_expr(binding, attrs, where_clause, bindings).to_tokens(&mut expr)
- }
-
- quote! {{
- let mut writer = style_traits::values::SequenceWriter::new(dest, #separator);
- #expr
- Ok(())
- }}
-}
-
-fn derive_single_field_expr(
- field: &BindingInfo,
- attrs: CssFieldAttrs,
- where_clause: &mut Option<WhereClause>,
- bindings: &[BindingInfo],
-) -> TokenStream {
- let mut expr = if attrs.iterable {
- if let Some(if_empty) = attrs.if_empty {
- return quote! {
- {
- let mut iter = #field.iter().peekable();
- if iter.peek().is_none() {
- writer.raw_item(#if_empty)?;
- } else {
- for item in iter {
- writer.item(&item)?;
- }
- }
- }
- };
- }
- quote! {
- for item in #field.iter() {
- writer.item(&item)?;
- }
- }
- } else if attrs.represents_keyword {
- let ident = field
- .ast()
- .ident
- .as_ref()
- .expect("Unnamed field with represents_keyword?");
- let ident = cg::to_css_identifier(&ident.to_string()).replace("_", "-");
- quote! {
- if *#field {
- writer.raw_item(#ident)?;
- }
- }
- } else {
- if attrs.field_bound {
- let ty = &field.ast().ty;
- cg::add_predicate(where_clause, parse_quote!(#ty: style_traits::ToCss));
- }
- quote! { writer.item(#field)?; }
- };
-
- if let Some(condition) = attrs.skip_if {
- expr = quote! {
- if !#condition(#field) {
- #expr
- }
- }
- }
-
- if let Some(condition) = attrs.contextual_skip_if {
- expr = quote! {
- if !#condition(#(#bindings), *) {
- #expr
- }
- }
- }
-
- expr
-}
-
-#[derive(Default, FromMeta)]
-#[darling(default)]
-pub struct CssBitflagAttrs {
- /// Flags that can only go on their own, comma-separated.
- pub single: Option<String>,
- /// Flags that can go mixed with each other, comma-separated.
- pub mixed: Option<String>,
- /// Extra validation of the resulting mixed flags.
- pub validate_mixed: Option<Path>,
- /// Whether there are overlapping bits we need to take care of when
- /// serializing.
- pub overlapping_bits: bool,
-}
-
-impl CssBitflagAttrs {
- /// Returns a vector of (rust_name, css_name) of a given flag list.
- fn names(s: &Option<String>) -> Vec<(String, String)> {
- let s = match s {
- Some(s) => s,
- None => return vec![],
- };
- s.split(',')
- .map(|css_name| (cg::to_scream_case(css_name), css_name.to_owned()))
- .collect()
- }
-
- pub fn single_flags(&self) -> Vec<(String, String)> {
- Self::names(&self.single)
- }
-
- pub fn mixed_flags(&self) -> Vec<(String, String)> {
- Self::names(&self.mixed)
- }
-}
-
-#[derive(Default, FromDeriveInput)]
-#[darling(attributes(css), default)]
-pub struct CssInputAttrs {
- pub derive_debug: bool,
- // Here because structs variants are also their whole type definition.
- pub function: Option<Override<String>>,
- // Here because structs variants are also their whole type definition.
- pub comma: bool,
- pub bitflags: Option<CssBitflagAttrs>,
-}
-
-#[derive(Default, FromVariant)]
-#[darling(attributes(css), default)]
-pub struct CssVariantAttrs {
- pub function: Option<Override<String>>,
- // Here because structs variants are also their whole type definition.
- pub derive_debug: bool,
- pub comma: bool,
- pub bitflags: Option<CssBitflagAttrs>,
- pub dimension: bool,
- pub keyword: Option<String>,
- pub skip: bool,
-}
-
-#[derive(Default, FromField)]
-#[darling(attributes(css), default)]
-pub struct CssFieldAttrs {
- pub if_empty: Option<String>,
- pub field_bound: bool,
- pub iterable: bool,
- pub skip: bool,
- pub represents_keyword: bool,
- pub contextual_skip_if: Option<Path>,
- pub skip_if: Option<Path>,
-}
diff --git a/components/style_derive/to_resolved_value.rs b/components/style_derive/to_resolved_value.rs
deleted file mode 100644
index e049f91152a..00000000000
--- a/components/style_derive/to_resolved_value.rs
+++ /dev/null
@@ -1,52 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
-
-use derive_common::cg;
-use proc_macro2::TokenStream;
-use syn::DeriveInput;
-use synstructure::BindStyle;
-use to_computed_value;
-
-pub fn derive(input: DeriveInput) -> TokenStream {
- let trait_impl = |from_body, to_body| {
- quote! {
- #[inline]
- fn from_resolved_value(from: Self::ResolvedValue) -> Self {
- #from_body
- }
-
- #[inline]
- fn to_resolved_value(
- self,
- context: &crate::values::resolved::Context,
- ) -> Self::ResolvedValue {
- #to_body
- }
- }
- };
-
- to_computed_value::derive_to_value(
- input,
- parse_quote!(crate::values::resolved::ToResolvedValue),
- parse_quote!(ResolvedValue),
- BindStyle::Move,
- |binding| {
- let attrs = cg::parse_field_attrs::<ResolvedValueAttrs>(&binding.ast());
- to_computed_value::ToValueAttrs {
- field_bound: attrs.field_bound,
- no_field_bound: attrs.no_field_bound,
- }
- },
- |binding| quote!(crate::values::resolved::ToResolvedValue::from_resolved_value(#binding)),
- |binding| quote!(crate::values::resolved::ToResolvedValue::to_resolved_value(#binding, context)),
- trait_impl,
- )
-}
-
-#[derive(Default, FromField)]
-#[darling(attributes(resolve), default)]
-struct ResolvedValueAttrs {
- field_bound: bool,
- no_field_bound: bool,
-}
diff --git a/components/style_static_prefs/Cargo.toml b/components/style_static_prefs/Cargo.toml
deleted file mode 100644
index 7a42225d8e9..00000000000
--- a/components/style_static_prefs/Cargo.toml
+++ /dev/null
@@ -1,7 +0,0 @@
-[package]
-name = "static_prefs"
-version = "0.1.0"
-edition = "2021"
-authors = ["The Servo Project Developers"]
-license = "MPL-2.0"
-publish = false
diff --git a/components/style_static_prefs/src/lib.rs b/components/style_static_prefs/src/lib.rs
deleted file mode 100644
index 818d80226c1..00000000000
--- a/components/style_static_prefs/src/lib.rs
+++ /dev/null
@@ -1,162 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
-
-//! A list of static preferences exposed to the style crate. These should
-//! be kept sync with the preferences used by the style.
-#[macro_export]
-macro_rules! pref {
- ("browser.display.permit_backplate") => {
- false
- };
- ("browser.display.use_document_fonts") => {
- false
- };
- ("dom.customHighlightAPI.enabled") => {
- false
- };
- ("dom.element.popover.enabled") => {
- false
- };
- ("gfx.font_rendering.opentype_svg.enabled") => {
- false
- };
- ("layout.css.color-mix.enabled") => {
- true
- };
- ("layout.css.contain-intrinsic-size.enabled") => {
- false
- };
- ("layout.css.container-queries.enabled") => {
- false
- };
- ("layout.css.content-visibility.enabled") => {
- false
- };
- ("layout.css.control-characters.visible") => {
- false
- };
- ("layout.css.cross-fade.enabled") => {
- false
- };
- ("layout.css.element-content-none.enabled") => {
- false
- };
- ("layout.css.fit-content-function.enabled") => {
- false
- };
- ("layout.css.font-palette.enabled") => {
- false
- };
- ("layout.css.font-tech.enabled") => {
- false
- };
- ("layout.css.font-variant-emoji.enabled") => {
- false
- };
- ("layout.css.font-variations.enabled") => {
- false
- };
- ("layout.css.forced-color-adjust.enabled") => {
- false
- };
- ("layout.css.forced-colors.enabled") => {
- false
- };
- ("layout.css.grid-template-masonry-value.enabled") => {
- false
- };
- ("layout.css.has-selector.enabled") => {
- false
- };
- ("layout.css.import-supports.enabled") => {
- false
- };
- ("layout.css.inverted-colors.enabled") => {
- false
- };
- ("layout.css.marker.restricted") => {
- false
- };
- ("layout.css.math-depth.enabled") => {
- false
- };
- ("layout.css.math-style.enabled") => {
- false
- };
- ("layout.css.more_color_4.enabled") => {
- true
- };
- ("layout.css.motion-path-offset-position.enabled") => {
- false
- };
- ("layout.css.motion-path-ray.enabled") => {
- false
- };
- ("layout.css.moz-control-character-visibility.enabled") => {
- false
- };
- ("layout.css.nesting.enabled") => {
- false
- };
- ("layout.css.overflow-moz-hidden-unscrollable.enabled") => {
- false
- };
- ("layout.css.overflow-overlay.enabled") => {
- false
- };
- ("layout.css.page-orientation.enabled") => {
- false
- };
- ("layout.css.prefers-contrast.enabled") => {
- false
- };
- ("layout.css.prefers-reduced-transparency.enabled") => {
- false
- };
- ("layout.css.properties-and-values.enabled") => {
- false
- };
- ("layout.css.scroll-driven-animations.enabled") => {
- false
- };
- ("layout.css.size-adjust.enabled") => {
- false
- };
- ("layout.css.stylo-local-work-queue.in-main-thread") => {
- 32
- };
- ("layout.css.stylo-local-work-queue.in-worker") => {
- 0
- };
- ("layout.css.stylo-threads") => {
- false
- };
- ("layout.css.stylo-work-unit-size") => {
- 16
- };
- ("layout.css.system-ui.enabled") => {
- false
- };
- ("layout.css.nan-inf.enabled") => {
- false
- };
- ("layout.css.trig.enabled") => {
- false
- };
- ("layout.css.round.enabled") => {
- false
- };
- ("layout.css.mod-rem.enabled") => {
- false
- };
- ("layout.css.exp.enabled") => {
- false
- };
- ("layout.css.bucket-attribute-names.enabled") => {
- false
- };
- ("layout.css.font-size-adjust.basis.enabled") => {
- false
- };
-}
diff --git a/components/style_traits/Cargo.toml b/components/style_traits/Cargo.toml
deleted file mode 100644
index 3b9ecd7aaed..00000000000
--- a/components/style_traits/Cargo.toml
+++ /dev/null
@@ -1,32 +0,0 @@
-[package]
-name = "style_traits"
-version = "0.0.1"
-authors = ["The Servo Project Developers"]
-license = "MPL-2.0"
-publish = false
-
-[lib]
-name = "style_traits"
-path = "lib.rs"
-
-[features]
-servo = ["servo_atoms", "cssparser/serde", "webrender_api", "url", "euclid/serde"]
-gecko = []
-
-[dependencies]
-app_units = "0.7"
-bitflags = "1.0"
-cssparser = { workspace = true }
-euclid = "0.22"
-lazy_static = "1"
-malloc_size_of = { path = "../malloc_size_of" }
-malloc_size_of_derive = "0.1"
-selectors = { path = "../selectors" }
-serde = "1.0"
-servo_arc = { path = "../servo_arc" }
-servo_atoms = { path = "../atoms", optional = true }
-size_of_test = { path = "../size_of_test" }
-to_shmem = { path = "../to_shmem" }
-to_shmem_derive = { path = "../to_shmem_derive" }
-url = { workspace = true, optional = true }
-webrender_api = { workspace = true, optional = true }
diff --git a/components/style_traits/arc_slice.rs b/components/style_traits/arc_slice.rs
deleted file mode 100644
index 1f10ed9455c..00000000000
--- a/components/style_traits/arc_slice.rs
+++ /dev/null
@@ -1,163 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
-
-//! A thin atomically-reference-counted slice.
-
-use serde::de::{Deserialize, Deserializer};
-use serde::ser::{Serialize, Serializer};
-use servo_arc::ThinArc;
-use std::ops::Deref;
-use std::ptr::NonNull;
-use std::{iter, mem};
-
-use malloc_size_of::{MallocSizeOf, MallocSizeOfOps, MallocUnconditionalSizeOf};
-
-/// A canary that we stash in ArcSlices.
-///
-/// Given we cannot use a zero-sized-type for the header, since well, C++
-/// doesn't have zsts, and we want to use cbindgen for this type, we may as well
-/// assert some sanity at runtime.
-///
-/// We use an u64, to guarantee that we can use a single singleton for every
-/// empty slice, even if the types they hold are aligned differently.
-const ARC_SLICE_CANARY: u64 = 0xf3f3f3f3f3f3f3f3;
-
-/// A wrapper type for a refcounted slice using ThinArc.
-///
-/// cbindgen:derive-eq=false
-/// cbindgen:derive-neq=false
-#[repr(C)]
-#[derive(Debug, Eq, PartialEq, ToShmem)]
-pub struct ArcSlice<T>(#[shmem(field_bound)] ThinArc<u64, T>);
-
-impl<T> Deref for ArcSlice<T> {
- type Target = [T];
-
- #[inline]
- fn deref(&self) -> &Self::Target {
- debug_assert_eq!(self.0.header.header, ARC_SLICE_CANARY);
- &self.0.slice
- }
-}
-
-impl<T> Clone for ArcSlice<T> {
- fn clone(&self) -> Self {
- ArcSlice(self.0.clone())
- }
-}
-
-lazy_static! {
- // ThinArc doesn't support alignments greater than align_of::<u64>.
- static ref EMPTY_ARC_SLICE: ArcSlice<u64> = {
- ArcSlice::from_iter_leaked(iter::empty())
- };
-}
-
-impl<T> Default for ArcSlice<T> {
- #[allow(unsafe_code)]
- fn default() -> Self {
- debug_assert!(
- mem::align_of::<T>() <= mem::align_of::<u64>(),
- "Need to increase the alignment of EMPTY_ARC_SLICE"
- );
- unsafe {
- let empty: ArcSlice<_> = EMPTY_ARC_SLICE.clone();
- let empty: Self = mem::transmute(empty);
- debug_assert_eq!(empty.len(), 0);
- empty
- }
- }
-}
-
-impl<T: Serialize> Serialize for ArcSlice<T> {
- fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
- where
- S: Serializer,
- {
- self.deref().serialize(serializer)
- }
-}
-
-impl<'de, T: Deserialize<'de>> Deserialize<'de> for ArcSlice<T> {
- fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
- where
- D: Deserializer<'de>,
- {
- let r = Vec::deserialize(deserializer)?;
- Ok(ArcSlice::from_iter(r.into_iter()))
- }
-}
-
-impl<T> ArcSlice<T> {
- /// Creates an Arc for a slice using the given iterator to generate the
- /// slice.
- #[inline]
- pub fn from_iter<I>(items: I) -> Self
- where
- I: Iterator<Item = T> + ExactSizeIterator,
- {
- if items.len() == 0 {
- return Self::default();
- }
- ArcSlice(ThinArc::from_header_and_iter(ARC_SLICE_CANARY, items))
- }
-
- /// Creates an Arc for a slice using the given iterator to generate the
- /// slice, and marks the arc as intentionally leaked from the refcount
- /// logging point of view.
- #[inline]
- pub fn from_iter_leaked<I>(items: I) -> Self
- where
- I: Iterator<Item = T> + ExactSizeIterator,
- {
- let thin_arc = ThinArc::from_header_and_iter(ARC_SLICE_CANARY, items);
- thin_arc.with_arc(|a| a.mark_as_intentionally_leaked());
- ArcSlice(thin_arc)
- }
-
- /// Creates a value that can be passed via FFI, and forgets this value
- /// altogether.
- #[inline]
- #[allow(unsafe_code)]
- pub fn forget(self) -> ForgottenArcSlicePtr<T> {
- let ret = unsafe {
- ForgottenArcSlicePtr(NonNull::new_unchecked(self.0.ptr() as *const _ as *mut _))
- };
- mem::forget(self);
- ret
- }
-
- /// Leaks an empty arc slice pointer, and returns it. Only to be used to
- /// construct ArcSlices from FFI.
- #[inline]
- pub fn leaked_empty_ptr() -> *mut std::os::raw::c_void {
- let empty: ArcSlice<_> = EMPTY_ARC_SLICE.clone();
- let ptr = empty.0.ptr();
- std::mem::forget(empty);
- ptr as *mut _
- }
-
- /// Returns whether there's only one reference to this ArcSlice.
- pub fn is_unique(&self) -> bool {
- self.0.with_arc(|arc| arc.is_unique())
- }
-}
-
-impl<T: MallocSizeOf> MallocUnconditionalSizeOf for ArcSlice<T> {
- #[allow(unsafe_code)]
- fn unconditional_size_of(&self, ops: &mut MallocSizeOfOps) -> usize {
- let mut size = unsafe { ops.malloc_size_of(self.0.heap_ptr()) };
- for el in self.iter() {
- size += el.size_of(ops);
- }
- size
- }
-}
-
-/// The inner pointer of an ArcSlice<T>, to be sent via FFI.
-/// The type of the pointer is a bit of a lie, we just want to preserve the type
-/// but these pointers cannot be constructed outside of this crate, so we're
-/// good.
-#[repr(C)]
-pub struct ForgottenArcSlicePtr<T>(NonNull<T>);
diff --git a/components/style_traits/dom.rs b/components/style_traits/dom.rs
deleted file mode 100644
index d8c660d2be9..00000000000
--- a/components/style_traits/dom.rs
+++ /dev/null
@@ -1,216 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
-
-//! Types used to access the DOM from style calculation.
-
-use malloc_size_of::malloc_size_of_is_0;
-
-/// An opaque handle to a node, which, unlike UnsafeNode, cannot be transformed
-/// back into a non-opaque representation. The only safe operation that can be
-/// performed on this node is to compare it to another opaque handle or to another
-/// OpaqueNode.
-///
-/// Layout and Graphics use this to safely represent nodes for comparison purposes.
-/// Because the script task's GC does not trace layout, node data cannot be safely stored in layout
-/// data structures. Also, layout code tends to be faster when the DOM is not being accessed, for
-/// locality reasons. Using `OpaqueNode` enforces this invariant.
-#[derive(Clone, Copy, Debug, Eq, Hash, MallocSizeOf, PartialEq)]
-#[cfg_attr(feature = "servo", derive(Deserialize, Serialize))]
-pub struct OpaqueNode(pub usize);
-
-impl OpaqueNode {
- /// Returns the address of this node, for debugging purposes.
- #[inline]
- pub fn id(&self) -> usize {
- self.0
- }
-}
-
-// DOM types to be shared between Rust and C++.
-
-bitflags! {
- /// Event-based element states.
- #[repr(C)]
- pub struct ElementState: u64 {
- /// The mouse is down on this element.
- /// <https://html.spec.whatwg.org/multipage/#selector-active>
- /// FIXME(#7333): set/unset this when appropriate
- const ACTIVE = 1 << 0;
- /// This element has focus.
- /// <https://html.spec.whatwg.org/multipage/#selector-focus>
- const FOCUS = 1 << 1;
- /// The mouse is hovering over this element.
- /// <https://html.spec.whatwg.org/multipage/#selector-hover>
- const HOVER = 1 << 2;
- /// Content is enabled (and can be disabled).
- /// <http://www.whatwg.org/html/#selector-enabled>
- const ENABLED = 1 << 3;
- /// Content is disabled.
- /// <http://www.whatwg.org/html/#selector-disabled>
- const DISABLED = 1 << 4;
- /// Content is checked.
- /// <https://html.spec.whatwg.org/multipage/#selector-checked>
- const CHECKED = 1 << 5;
- /// <https://html.spec.whatwg.org/multipage/#selector-indeterminate>
- const INDETERMINATE = 1 << 6;
- /// <https://html.spec.whatwg.org/multipage/#selector-placeholder-shown>
- const PLACEHOLDER_SHOWN = 1 << 7;
- /// <https://html.spec.whatwg.org/multipage/#selector-target>
- const URLTARGET = 1 << 8;
- /// <https://fullscreen.spec.whatwg.org/#%3Afullscreen-pseudo-class>
- const FULLSCREEN = 1 << 9;
- /// <https://html.spec.whatwg.org/multipage/#selector-valid>
- const VALID = 1 << 10;
- /// <https://html.spec.whatwg.org/multipage/#selector-invalid>
- const INVALID = 1 << 11;
- /// <https://drafts.csswg.org/selectors-4/#user-valid-pseudo>
- const USER_VALID = 1 << 12;
- /// <https://drafts.csswg.org/selectors-4/#user-invalid-pseudo>
- const USER_INVALID = 1 << 13;
- /// Non-standard: https://developer.mozilla.org/en-US/docs/Web/CSS/:-moz-broken
- const BROKEN = 1 << 14;
- /// Non-standard: https://developer.mozilla.org/en-US/docs/Web/CSS/:-moz-loading
- const LOADING = 1 << 15;
- /// <https://html.spec.whatwg.org/multipage/#selector-required>
- const REQUIRED = 1 << 16;
- /// <https://html.spec.whatwg.org/multipage/#selector-optional>
- /// We use an underscore to workaround a silly windows.h define.
- const OPTIONAL_ = 1 << 17;
- /// <https://html.spec.whatwg.org/multipage/#selector-defined>
- const DEFINED = 1 << 18;
- /// <https://html.spec.whatwg.org/multipage/#selector-visited>
- const VISITED = 1 << 19;
- /// <https://html.spec.whatwg.org/multipage/#selector-link>
- const UNVISITED = 1 << 20;
- /// <https://drafts.csswg.org/selectors-4/#the-any-link-pseudo>
- const VISITED_OR_UNVISITED = Self::VISITED.bits | Self::UNVISITED.bits;
- /// Non-standard: https://developer.mozilla.org/en-US/docs/Web/CSS/:-moz-drag-over
- const DRAGOVER = 1 << 21;
- /// <https://html.spec.whatwg.org/multipage/#selector-in-range>
- const INRANGE = 1 << 22;
- /// <https://html.spec.whatwg.org/multipage/#selector-out-of-range>
- const OUTOFRANGE = 1 << 23;
- /// <https://html.spec.whatwg.org/multipage/#selector-read-only>
- const READONLY = 1 << 24;
- /// <https://html.spec.whatwg.org/multipage/#selector-read-write>
- const READWRITE = 1 << 25;
- /// <https://html.spec.whatwg.org/multipage/#selector-default>
- const DEFAULT = 1 << 26;
- /// Non-standard & undocumented.
- const OPTIMUM = 1 << 28;
- /// Non-standard & undocumented.
- const SUB_OPTIMUM = 1 << 29;
- /// Non-standard & undocumented.
- const SUB_SUB_OPTIMUM = 1 << 30;
- /// Non-standard & undocumented.
- const INCREMENT_SCRIPT_LEVEL = 1u64 << 31;
- /// <https://drafts.csswg.org/selectors-4/#the-focus-visible-pseudo>
- const FOCUSRING = 1u64 << 32;
- /// <https://drafts.csswg.org/selectors-4/#the-focus-within-pseudo>
- const FOCUS_WITHIN = 1u64 << 33;
- /// :dir matching; the states are used for dynamic change detection.
- /// State that elements that match :dir(ltr) are in.
- const LTR = 1u64 << 34;
- /// State that elements that match :dir(rtl) are in.
- const RTL = 1u64 << 35;
- /// State that HTML elements that have a "dir" attr are in.
- const HAS_DIR_ATTR = 1u64 << 36;
- /// State that HTML elements with dir="ltr" (or something
- /// case-insensitively equal to "ltr") are in.
- const HAS_DIR_ATTR_LTR = 1u64 << 37;
- /// State that HTML elements with dir="rtl" (or something
- /// case-insensitively equal to "rtl") are in.
- const HAS_DIR_ATTR_RTL = 1u64 << 38;
- /// State that HTML <bdi> elements without a valid-valued "dir" attr or
- /// any HTML elements (including <bdi>) with dir="auto" (or something
- /// case-insensitively equal to "auto") are in.
- const HAS_DIR_ATTR_LIKE_AUTO = 1u64 << 39;
- /// Non-standard & undocumented.
- const AUTOFILL = 1u64 << 40;
- /// Non-standard & undocumented.
- const AUTOFILL_PREVIEW = 1u64 << 41;
- /// State that dialog element is modal, for centered alignment
- /// <https://html.spec.whatwg.org/multipage/#centered-alignment>
- const MODAL_DIALOG = 1u64 << 42;
- /// <https://html.spec.whatwg.org/multipage/#inert-subtrees>
- const INERT = 1u64 << 43;
- /// State for the topmost modal element in top layer
- const TOPMOST_MODAL = 1u64 << 44;
- /// Initially used for the devtools highlighter, but now somehow only
- /// used for the devtools accessibility inspector.
- const DEVTOOLS_HIGHLIGHTED = 1u64 << 45;
- /// Used for the devtools style editor. Probably should go away.
- const STYLEEDITOR_TRANSITIONING = 1u64 << 46;
- /// For :-moz-value-empty (to show widgets like the reveal password
- /// button or the clear button).
- const VALUE_EMPTY = 1u64 << 47;
- /// For :-moz-revealed.
- const REVEALED = 1u64 << 48;
-
- /// Some convenience unions.
- const DIR_STATES = Self::LTR.bits | Self::RTL.bits;
-
- const DIR_ATTR_STATES = Self::HAS_DIR_ATTR.bits |
- Self::HAS_DIR_ATTR_LTR.bits |
- Self::HAS_DIR_ATTR_RTL.bits |
- Self::HAS_DIR_ATTR_LIKE_AUTO.bits;
-
- const DISABLED_STATES = Self::DISABLED.bits | Self::ENABLED.bits;
-
- const REQUIRED_STATES = Self::REQUIRED.bits | Self::OPTIONAL_.bits;
-
- /// Event states that can be added and removed through
- /// Element::{Add,Remove}ManuallyManagedStates.
- ///
- /// Take care when manually managing state bits. You are responsible
- /// for setting or clearing the bit when an Element is added or removed
- /// from a document (e.g. in BindToTree and UnbindFromTree), if that is
- /// an appropriate thing to do for your state bit.
- const MANUALLY_MANAGED_STATES = Self::AUTOFILL.bits | Self::AUTOFILL_PREVIEW.bits;
-
- /// Event states that are managed externally to an element (by the
- /// EventStateManager, or by other code). As opposed to those in
- /// INTRINSIC_STATES, which are are computed by the element itself
- /// and returned from Element::IntrinsicState.
- const EXTERNALLY_MANAGED_STATES =
- Self::MANUALLY_MANAGED_STATES.bits |
- Self::DIR_ATTR_STATES.bits |
- Self::DISABLED_STATES.bits |
- Self::REQUIRED_STATES.bits |
- Self::ACTIVE.bits |
- Self::DEFINED.bits |
- Self::DRAGOVER.bits |
- Self::FOCUS.bits |
- Self::FOCUSRING.bits |
- Self::FOCUS_WITHIN.bits |
- Self::FULLSCREEN.bits |
- Self::HOVER.bits |
- Self::URLTARGET.bits |
- Self::MODAL_DIALOG.bits |
- Self::INERT.bits |
- Self::TOPMOST_MODAL.bits |
- Self::REVEALED.bits;
-
- const INTRINSIC_STATES = !Self::EXTERNALLY_MANAGED_STATES.bits;
- }
-}
-
-bitflags! {
- /// Event-based document states.
- #[repr(C)]
- pub struct DocumentState: u64 {
- /// Window activation status
- const WINDOW_INACTIVE = 1 << 0;
- /// RTL locale: specific to the XUL localedir attribute
- const RTL_LOCALE = 1 << 1;
- /// LTR locale: specific to the XUL localedir attribute
- const LTR_LOCALE = 1 << 2;
- /// LWTheme status
- const LWTHEME = 1 << 3;
-
- const ALL_LOCALEDIR_BITS = Self::LTR_LOCALE.bits | Self::RTL_LOCALE.bits;
- }
-}
-
-malloc_size_of_is_0!(ElementState, DocumentState);
diff --git a/components/style_traits/lib.rs b/components/style_traits/lib.rs
deleted file mode 100644
index 334b3a891bd..00000000000
--- a/components/style_traits/lib.rs
+++ /dev/null
@@ -1,281 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
-
-//! This module contains shared types and messages for use by devtools/script.
-//! The traits are here instead of in script so that the devtools crate can be
-//! modified independently of the rest of Servo.
-
-#![crate_name = "style_traits"]
-#![crate_type = "rlib"]
-
-extern crate app_units;
-#[macro_use]
-extern crate bitflags;
-extern crate cssparser;
-extern crate euclid;
-#[macro_use]
-extern crate lazy_static;
-extern crate malloc_size_of;
-#[macro_use]
-extern crate malloc_size_of_derive;
-extern crate selectors;
-#[macro_use]
-extern crate serde;
-extern crate servo_arc;
-#[cfg(feature = "servo")]
-extern crate servo_atoms;
-extern crate to_shmem;
-#[macro_use]
-extern crate to_shmem_derive;
-#[cfg(feature = "servo")]
-extern crate webrender_api;
-#[cfg(feature = "servo")]
-extern crate url;
-#[cfg(feature = "servo")]
-pub use webrender_api::units::DevicePixel;
-
-use cssparser::{CowRcStr, Token};
-use selectors::parser::SelectorParseErrorKind;
-#[cfg(feature = "servo")]
-use servo_atoms::Atom;
-
-/// One hardware pixel.
-///
-/// This unit corresponds to the smallest addressable element of the display hardware.
-#[cfg(not(feature = "servo"))]
-#[derive(Clone, Copy, Debug)]
-pub enum DevicePixel {}
-
-/// Represents a mobile style pinch zoom factor.
-/// TODO(gw): Once WR supports pinch zoom, use a type directly from webrender_api.
-#[derive(Clone, Copy, Debug, PartialEq)]
-#[cfg_attr(feature = "servo", derive(Deserialize, Serialize, MallocSizeOf))]
-pub struct PinchZoomFactor(f32);
-
-impl PinchZoomFactor {
- /// Construct a new pinch zoom factor.
- pub fn new(scale: f32) -> PinchZoomFactor {
- PinchZoomFactor(scale)
- }
-
- /// Get the pinch zoom factor as an untyped float.
- pub fn get(&self) -> f32 {
- self.0
- }
-}
-
-/// One CSS "px" in the coordinate system of the "initial viewport":
-/// <http://www.w3.org/TR/css-device-adapt/#initial-viewport>
-///
-/// `CSSPixel` is equal to `DeviceIndependentPixel` times a "page zoom" factor controlled by the user. This is
-/// the desktop-style "full page" zoom that enlarges content but then reflows the layout viewport
-/// so it still exactly fits the visible area.
-///
-/// At the default zoom level of 100%, one `CSSPixel` is equal to one `DeviceIndependentPixel`. However, if the
-/// document is zoomed in or out then this scale may be larger or smaller.
-#[derive(Clone, Copy, Debug)]
-pub enum CSSPixel {}
-
-// In summary, the hierarchy of pixel units and the factors to convert from one to the next:
-//
-// DevicePixel
-// / hidpi_ratio => DeviceIndependentPixel
-// / desktop_zoom => CSSPixel
-
-pub mod arc_slice;
-pub mod dom;
-pub mod specified_value_info;
-#[macro_use]
-pub mod values;
-pub mod owned_slice;
-pub mod owned_str;
-
-pub use crate::specified_value_info::{CssType, KeywordsCollectFn, SpecifiedValueInfo};
-pub use crate::values::{
- Comma, CommaWithSpace, CssWriter, OneOrMoreSeparated, Separator, Space, ToCss,
-};
-
-/// The error type for all CSS parsing routines.
-pub type ParseError<'i> = cssparser::ParseError<'i, StyleParseErrorKind<'i>>;
-
-/// Error in property value parsing
-pub type ValueParseError<'i> = cssparser::ParseError<'i, ValueParseErrorKind<'i>>;
-
-#[derive(Clone, Debug, PartialEq)]
-/// Errors that can be encountered while parsing CSS values.
-pub enum StyleParseErrorKind<'i> {
- /// A bad URL token in a DVB.
- BadUrlInDeclarationValueBlock(CowRcStr<'i>),
- /// A bad string token in a DVB.
- BadStringInDeclarationValueBlock(CowRcStr<'i>),
- /// Unexpected closing parenthesis in a DVB.
- UnbalancedCloseParenthesisInDeclarationValueBlock,
- /// Unexpected closing bracket in a DVB.
- UnbalancedCloseSquareBracketInDeclarationValueBlock,
- /// Unexpected closing curly bracket in a DVB.
- UnbalancedCloseCurlyBracketInDeclarationValueBlock,
- /// A property declaration value had input remaining after successfully parsing.
- PropertyDeclarationValueNotExhausted,
- /// An unexpected dimension token was encountered.
- UnexpectedDimension(CowRcStr<'i>),
- /// Missing or invalid media feature name.
- MediaQueryExpectedFeatureName(CowRcStr<'i>),
- /// Missing or invalid media feature value.
- MediaQueryExpectedFeatureValue,
- /// A media feature range operator was not expected.
- MediaQueryUnexpectedOperator,
- /// min- or max- properties must have a value.
- RangedExpressionWithNoValue,
- /// A function was encountered that was not expected.
- UnexpectedFunction(CowRcStr<'i>),
- /// Error encountered parsing a @property's `syntax` descriptor
- PropertySyntaxField(PropertySyntaxParseError),
- /// @namespace must be before any rule but @charset and @import
- UnexpectedNamespaceRule,
- /// @import must be before any rule but @charset
- UnexpectedImportRule,
- /// @import rules are disallowed in the parser.
- DisallowedImportRule,
- /// Unexpected @charset rule encountered.
- UnexpectedCharsetRule,
- /// The @property `<custom-property-name>` must start with `--`
- UnexpectedIdent(CowRcStr<'i>),
- /// A placeholder for many sources of errors that require more specific variants.
- UnspecifiedError,
- /// An unexpected token was found within a namespace rule.
- UnexpectedTokenWithinNamespace(Token<'i>),
- /// An error was encountered while parsing a property value.
- ValueError(ValueParseErrorKind<'i>),
- /// An error was encountered while parsing a selector
- SelectorError(SelectorParseErrorKind<'i>),
- /// The property declaration was for an unknown property.
- UnknownProperty(CowRcStr<'i>),
- /// The property declaration was for a disabled experimental property.
- ExperimentalProperty,
- /// The property declaration contained an invalid color value.
- InvalidColor(CowRcStr<'i>, Token<'i>),
- /// The property declaration contained an invalid filter value.
- InvalidFilter(CowRcStr<'i>, Token<'i>),
- /// The property declaration contained an invalid value.
- OtherInvalidValue(CowRcStr<'i>),
- /// The declaration contained an animation property, and we were parsing
- /// this as a keyframe block (so that property should be ignored).
- ///
- /// See: https://drafts.csswg.org/css-animations/#keyframes
- AnimationPropertyInKeyframeBlock,
- /// The property is not allowed within a page rule.
- NotAllowedInPageRule,
-}
-
-impl<'i> From<ValueParseErrorKind<'i>> for StyleParseErrorKind<'i> {
- fn from(this: ValueParseErrorKind<'i>) -> Self {
- StyleParseErrorKind::ValueError(this)
- }
-}
-
-impl<'i> From<SelectorParseErrorKind<'i>> for StyleParseErrorKind<'i> {
- fn from(this: SelectorParseErrorKind<'i>) -> Self {
- StyleParseErrorKind::SelectorError(this)
- }
-}
-
-/// Specific errors that can be encountered while parsing property values.
-#[derive(Clone, Debug, PartialEq)]
-pub enum ValueParseErrorKind<'i> {
- /// An invalid token was encountered while parsing a color value.
- InvalidColor(Token<'i>),
- /// An invalid filter value was encountered.
- InvalidFilter(Token<'i>),
-}
-
-impl<'i> StyleParseErrorKind<'i> {
- /// Create an InvalidValue parse error
- pub fn new_invalid<S>(name: S, value_error: ParseError<'i>) -> ParseError<'i>
- where
- S: Into<CowRcStr<'i>>,
- {
- let name = name.into();
- let variant = match value_error.kind {
- cssparser::ParseErrorKind::Custom(StyleParseErrorKind::ValueError(e)) => match e {
- ValueParseErrorKind::InvalidColor(token) => {
- StyleParseErrorKind::InvalidColor(name, token)
- },
- ValueParseErrorKind::InvalidFilter(token) => {
- StyleParseErrorKind::InvalidFilter(name, token)
- },
- },
- _ => StyleParseErrorKind::OtherInvalidValue(name),
- };
- cssparser::ParseError {
- kind: cssparser::ParseErrorKind::Custom(variant),
- location: value_error.location,
- }
- }
-}
-
-/// Errors that can be encountered while parsing the @property rule's syntax descriptor.
-#[derive(Clone, Debug, PartialEq)]
-pub enum PropertySyntaxParseError {
- /// The string's length was 0.
- EmptyInput,
- /// A non-whitespace, non-pipe character was fount after parsing a component.
- ExpectedPipeBetweenComponents,
- /// The start of an identifier was expected but not found.
- ///
- /// <https://drafts.csswg.org/css-syntax-3/#name-start-code-point>
- InvalidNameStart,
- /// The name is not a valid `<ident>`.
- InvalidName,
- /// The data type name was not closed.
- ///
- /// <https://drafts.css-houdini.org/css-properties-values-api-1/#consume-data-type-name>
- UnclosedDataTypeName,
- /// The next byte was expected while parsing, but EOF was found instead.
- UnexpectedEOF,
- /// The data type is not a supported syntax component name.
- ///
- /// <https://drafts.css-houdini.org/css-properties-values-api-1/#supported-names>
- UnknownDataTypeName,
-}
-
-bitflags! {
- /// The mode to use when parsing values.
- pub struct ParsingMode: u8 {
- /// In CSS; lengths must have units, except for zero values, where the unit can be omitted.
- /// <https://www.w3.org/TR/css3-values/#lengths>
- const DEFAULT = 0x00;
- /// In SVG; a coordinate or length value without a unit identifier (e.g., "25") is assumed
- /// to be in user units (px).
- /// <https://www.w3.org/TR/SVG/coords.html#Units>
- const ALLOW_UNITLESS_LENGTH = 0x01;
- /// In SVG; out-of-range values are not treated as an error in parsing.
- /// <https://www.w3.org/TR/SVG/implnote.html#RangeClamping>
- const ALLOW_ALL_NUMERIC_VALUES = 0x02;
- }
-}
-
-impl ParsingMode {
- /// Whether the parsing mode allows unitless lengths for non-zero values to be intpreted as px.
- #[inline]
- pub fn allows_unitless_lengths(&self) -> bool {
- self.intersects(ParsingMode::ALLOW_UNITLESS_LENGTH)
- }
-
- /// Whether the parsing mode allows all numeric values.
- #[inline]
- pub fn allows_all_numeric_values(&self) -> bool {
- self.intersects(ParsingMode::ALLOW_ALL_NUMERIC_VALUES)
- }
-}
-
-#[cfg(feature = "servo")]
-/// Speculatively execute paint code in the worklet thread pool.
-pub trait SpeculativePainter: Send + Sync {
- /// <https://drafts.css-houdini.org/css-paint-api/#draw-a-paint-image>
- fn speculatively_draw_a_paint_image(
- &self,
- properties: Vec<(Atom, String)>,
- arguments: Vec<String>,
- );
-}
diff --git a/components/style_traits/owned_slice.rs b/components/style_traits/owned_slice.rs
deleted file mode 100644
index 36ba3162e59..00000000000
--- a/components/style_traits/owned_slice.rs
+++ /dev/null
@@ -1,198 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
-
-#![allow(unsafe_code)]
-
-//! A replacement for `Box<[T]>` that cbindgen can understand.
-
-use malloc_size_of::{MallocShallowSizeOf, MallocSizeOf, MallocSizeOfOps};
-use serde::de::{Deserialize, Deserializer};
-use serde::ser::{Serialize, Serializer};
-use std::marker::PhantomData;
-use std::ops::{Deref, DerefMut};
-use std::ptr::NonNull;
-use std::{fmt, iter, mem, slice};
-use to_shmem::{self, SharedMemoryBuilder, ToShmem};
-
-/// A struct that basically replaces a `Box<[T]>`, but which cbindgen can
-/// understand.
-///
-/// We could rely on the struct layout of `Box<[T]>` per:
-///
-/// https://github.com/rust-lang/unsafe-code-guidelines/blob/master/reference/src/layout/pointers.md
-///
-/// But handling fat pointers with cbindgen both in structs and argument
-/// positions more generally is a bit tricky.
-///
-/// cbindgen:derive-eq=false
-/// cbindgen:derive-neq=false
-#[repr(C)]
-pub struct OwnedSlice<T: Sized> {
- ptr: NonNull<T>,
- len: usize,
- _phantom: PhantomData<T>,
-}
-
-impl<T: Sized> Default for OwnedSlice<T> {
- #[inline]
- fn default() -> Self {
- Self {
- len: 0,
- ptr: NonNull::dangling(),
- _phantom: PhantomData,
- }
- }
-}
-
-impl<T: Sized> Drop for OwnedSlice<T> {
- #[inline]
- fn drop(&mut self) {
- if self.len != 0 {
- let _ = mem::replace(self, Self::default()).into_vec();
- }
- }
-}
-
-unsafe impl<T: Sized + Send> Send for OwnedSlice<T> {}
-unsafe impl<T: Sized + Sync> Sync for OwnedSlice<T> {}
-
-impl<T: Clone> Clone for OwnedSlice<T> {
- #[inline]
- fn clone(&self) -> Self {
- Self::from_slice(&**self)
- }
-}
-
-impl<T: fmt::Debug> fmt::Debug for OwnedSlice<T> {
- fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
- self.deref().fmt(formatter)
- }
-}
-
-impl<T: PartialEq> PartialEq for OwnedSlice<T> {
- fn eq(&self, other: &Self) -> bool {
- self.deref().eq(other.deref())
- }
-}
-
-impl<T: Eq> Eq for OwnedSlice<T> {}
-
-impl<T: Sized> OwnedSlice<T> {
- /// Convert the OwnedSlice into a boxed slice.
- #[inline]
- pub fn into_box(self) -> Box<[T]> {
- self.into_vec().into_boxed_slice()
- }
-
- /// Convert the OwnedSlice into a Vec.
- #[inline]
- pub fn into_vec(self) -> Vec<T> {
- let ret = unsafe { Vec::from_raw_parts(self.ptr.as_ptr(), self.len, self.len) };
- mem::forget(self);
- ret
- }
-
- /// Convert the regular slice into an owned slice.
- #[inline]
- pub fn from_slice(s: &[T]) -> Self
- where
- T: Clone,
- {
- Self::from(s.to_vec())
- }
-}
-
-impl<T> IntoIterator for OwnedSlice<T> {
- type Item = T;
- type IntoIter = <Vec<T> as IntoIterator>::IntoIter;
-
- #[inline]
- fn into_iter(self) -> Self::IntoIter {
- self.into_vec().into_iter()
- }
-}
-
-impl<T> Deref for OwnedSlice<T> {
- type Target = [T];
-
- #[inline(always)]
- fn deref(&self) -> &Self::Target {
- unsafe { slice::from_raw_parts(self.ptr.as_ptr(), self.len) }
- }
-}
-
-impl<T> DerefMut for OwnedSlice<T> {
- #[inline(always)]
- fn deref_mut(&mut self) -> &mut Self::Target {
- unsafe { slice::from_raw_parts_mut(self.ptr.as_ptr(), self.len) }
- }
-}
-
-impl<T> From<Box<[T]>> for OwnedSlice<T> {
- #[inline]
- fn from(mut b: Box<[T]>) -> Self {
- let len = b.len();
- let ptr = unsafe { NonNull::new_unchecked(b.as_mut_ptr()) };
- mem::forget(b);
- Self {
- len,
- ptr,
- _phantom: PhantomData,
- }
- }
-}
-
-impl<T> From<Vec<T>> for OwnedSlice<T> {
- #[inline]
- fn from(b: Vec<T>) -> Self {
- Self::from(b.into_boxed_slice())
- }
-}
-
-impl<T: Sized> MallocShallowSizeOf for OwnedSlice<T> {
- fn shallow_size_of(&self, ops: &mut MallocSizeOfOps) -> usize {
- unsafe { ops.malloc_size_of(self.ptr.as_ptr()) }
- }
-}
-
-impl<T: MallocSizeOf + Sized> MallocSizeOf for OwnedSlice<T> {
- fn size_of(&self, ops: &mut MallocSizeOfOps) -> usize {
- self.shallow_size_of(ops) + (**self).size_of(ops)
- }
-}
-
-impl<T: ToShmem + Sized> ToShmem for OwnedSlice<T> {
- fn to_shmem(&self, builder: &mut SharedMemoryBuilder) -> to_shmem::Result<Self> {
- unsafe {
- let dest = to_shmem::to_shmem_slice(self.iter(), builder)?;
- Ok(mem::ManuallyDrop::new(Self::from(Box::from_raw(dest))))
- }
- }
-}
-
-impl<T> iter::FromIterator<T> for OwnedSlice<T> {
- #[inline]
- fn from_iter<I: IntoIterator<Item = T>>(iter: I) -> Self {
- Vec::from_iter(iter).into()
- }
-}
-
-impl<T: Serialize> Serialize for OwnedSlice<T> {
- fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
- where
- S: Serializer,
- {
- self.deref().serialize(serializer)
- }
-}
-
-impl<'de, T: Deserialize<'de>> Deserialize<'de> for OwnedSlice<T> {
- fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
- where
- D: Deserializer<'de>,
- {
- let r = Box::<[T]>::deserialize(deserializer)?;
- Ok(r.into())
- }
-}
diff --git a/components/style_traits/owned_str.rs b/components/style_traits/owned_str.rs
deleted file mode 100644
index ebfdcd5e066..00000000000
--- a/components/style_traits/owned_str.rs
+++ /dev/null
@@ -1,81 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
-
-#![allow(unsafe_code)]
-
-//! A replacement for `Box<str>` that has a defined layout for FFI.
-
-use crate::owned_slice::OwnedSlice;
-use std::fmt;
-use std::ops::{Deref, DerefMut};
-
-/// A struct that basically replaces a Box<str>, but with a defined layout,
-/// suitable for FFI.
-#[repr(C)]
-#[derive(Clone, Default, Eq, MallocSizeOf, PartialEq, ToShmem)]
-pub struct OwnedStr(OwnedSlice<u8>);
-
-impl fmt::Debug for OwnedStr {
- fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
- self.deref().fmt(formatter)
- }
-}
-
-impl Deref for OwnedStr {
- type Target = str;
-
- #[inline(always)]
- fn deref(&self) -> &Self::Target {
- unsafe { std::str::from_utf8_unchecked(&*self.0) }
- }
-}
-
-impl DerefMut for OwnedStr {
- #[inline(always)]
- fn deref_mut(&mut self) -> &mut Self::Target {
- unsafe { std::str::from_utf8_unchecked_mut(&mut *self.0) }
- }
-}
-
-impl OwnedStr {
- /// Convert the OwnedStr into a boxed str.
- #[inline]
- pub fn into_box(self) -> Box<str> {
- self.into_string().into_boxed_str()
- }
-
- /// Convert the OwnedStr into a `String`.
- #[inline]
- pub fn into_string(self) -> String {
- unsafe { String::from_utf8_unchecked(self.0.into_vec()) }
- }
-}
-
-impl From<OwnedStr> for String {
- #[inline]
- fn from(b: OwnedStr) -> Self {
- b.into_string()
- }
-}
-
-impl From<OwnedStr> for Box<str> {
- #[inline]
- fn from(b: OwnedStr) -> Self {
- b.into_box()
- }
-}
-
-impl From<Box<str>> for OwnedStr {
- #[inline]
- fn from(b: Box<str>) -> Self {
- Self::from(b.into_string())
- }
-}
-
-impl From<String> for OwnedStr {
- #[inline]
- fn from(s: String) -> Self {
- OwnedStr(s.into_bytes().into())
- }
-}
diff --git a/components/style_traits/rustfmt.toml b/components/style_traits/rustfmt.toml
deleted file mode 100644
index c7ad93bafe3..00000000000
--- a/components/style_traits/rustfmt.toml
+++ /dev/null
@@ -1 +0,0 @@
-disable_all_formatting = true
diff --git a/components/style_traits/specified_value_info.rs b/components/style_traits/specified_value_info.rs
deleted file mode 100644
index b73d576715a..00000000000
--- a/components/style_traits/specified_value_info.rs
+++ /dev/null
@@ -1,138 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
-
-//! Value information for devtools.
-
-use crate::arc_slice::ArcSlice;
-use crate::owned_slice::OwnedSlice;
-use servo_arc::Arc;
-use std::ops::Range;
-use std::sync::Arc as StdArc;
-
-/// Type of value that a property supports. This is used by Gecko's
-/// devtools to make sense about value it parses, and types listed
-/// here should match InspectorPropertyType in InspectorUtils.webidl.
-///
-/// XXX This should really be a bitflags rather than a namespace mod,
-/// but currently we cannot use bitflags in const.
-#[allow(non_snake_case)]
-pub mod CssType {
- /// <color>
- pub const COLOR: u8 = 1 << 0;
- /// <gradient>
- pub const GRADIENT: u8 = 1 << 1;
- /// <timing-function>
- pub const TIMING_FUNCTION: u8 = 1 << 2;
-}
-
-/// See SpecifiedValueInfo::collect_completion_keywords.
-pub type KeywordsCollectFn<'a> = &'a mut dyn FnMut(&[&'static str]);
-
-/// Information of values of a given specified value type.
-///
-/// This trait is derivable with `#[derive(SpecifiedValueInfo)]`.
-///
-/// The algorithm traverses the type definition. For `SUPPORTED_TYPES`,
-/// it puts an or'ed value of `SUPPORTED_TYPES` of all types it finds.
-/// For `collect_completion_keywords`, it recursively invokes this
-/// method on types found, and lists all keyword values and function
-/// names following the same rule as `ToCss` in that method.
-///
-/// Some attributes of `ToCss` can affect the behavior, specifically:
-/// * If `#[css(function)]` is found, the content inside the annotated
-/// variant (or the whole type) isn't traversed, only the function
-/// name is listed in `collect_completion_keywords`.
-/// * If `#[css(skip)]` is found, the content inside the variant or
-/// field is ignored.
-/// * Values listed in `#[css(if_empty)]`, `#[parse(aliases)]`, and
-/// `#[css(keyword)]` are added into `collect_completion_keywords`.
-///
-/// In addition to `css` attributes, it also has `value_info` helper
-/// attributes, including:
-/// * `#[value_info(ty = "TYPE")]` can be used to specify a constant
-/// from `CssType` to `SUPPORTED_TYPES`.
-/// * `#[value_info(other_values = "value1,value2")]` can be used to
-/// add other values related to a field, variant, or the type itself
-/// into `collect_completion_keywords`.
-/// * `#[value_info(starts_with_keyword)]` can be used on variants to
-/// add the name of a non-unit variant (serialized like `ToCss`) into
-/// `collect_completion_keywords`.
-pub trait SpecifiedValueInfo {
- /// Supported CssTypes by the given value type.
- ///
- /// XXX This should be typed CssType when that becomes a bitflags.
- /// Currently we cannot do so since bitflags cannot be used in constant.
- const SUPPORTED_TYPES: u8 = 0;
-
- /// Collect value starting words for the given specified value type.
- /// This includes keyword and function names which can appear at the
- /// beginning of a value of this type.
- ///
- /// Caller should pass in a callback function to accept the list of
- /// values. The callback function can be called multiple times, and
- /// some values passed to the callback may be duplicate.
- fn collect_completion_keywords(_f: KeywordsCollectFn) {}
-}
-
-impl SpecifiedValueInfo for bool {}
-impl SpecifiedValueInfo for f32 {}
-impl SpecifiedValueInfo for i8 {}
-impl SpecifiedValueInfo for i32 {}
-impl SpecifiedValueInfo for u8 {}
-impl SpecifiedValueInfo for u16 {}
-impl SpecifiedValueInfo for u32 {}
-impl SpecifiedValueInfo for usize {}
-impl SpecifiedValueInfo for str {}
-impl SpecifiedValueInfo for String {}
-impl SpecifiedValueInfo for crate::owned_str::OwnedStr {}
-
-#[cfg(feature = "servo")]
-impl SpecifiedValueInfo for ::servo_atoms::Atom {}
-#[cfg(feature = "servo")]
-impl SpecifiedValueInfo for ::url::Url {}
-
-impl<T: SpecifiedValueInfo + ?Sized> SpecifiedValueInfo for Box<T> {
- const SUPPORTED_TYPES: u8 = T::SUPPORTED_TYPES;
- fn collect_completion_keywords(f: KeywordsCollectFn) {
- T::collect_completion_keywords(f);
- }
-}
-
-impl<T: SpecifiedValueInfo> SpecifiedValueInfo for [T] {
- const SUPPORTED_TYPES: u8 = T::SUPPORTED_TYPES;
- fn collect_completion_keywords(f: KeywordsCollectFn) {
- T::collect_completion_keywords(f);
- }
-}
-
-macro_rules! impl_generic_specified_value_info {
- ($ty:ident<$param:ident>) => {
- impl<$param: SpecifiedValueInfo> SpecifiedValueInfo for $ty<$param> {
- const SUPPORTED_TYPES: u8 = $param::SUPPORTED_TYPES;
- fn collect_completion_keywords(f: KeywordsCollectFn) {
- $param::collect_completion_keywords(f);
- }
- }
- };
-}
-impl_generic_specified_value_info!(Option<T>);
-impl_generic_specified_value_info!(OwnedSlice<T>);
-impl_generic_specified_value_info!(Vec<T>);
-impl_generic_specified_value_info!(Arc<T>);
-impl_generic_specified_value_info!(StdArc<T>);
-impl_generic_specified_value_info!(ArcSlice<T>);
-impl_generic_specified_value_info!(Range<Idx>);
-
-impl<T1, T2> SpecifiedValueInfo for (T1, T2)
-where
- T1: SpecifiedValueInfo,
- T2: SpecifiedValueInfo,
-{
- const SUPPORTED_TYPES: u8 = T1::SUPPORTED_TYPES | T2::SUPPORTED_TYPES;
-
- fn collect_completion_keywords(f: KeywordsCollectFn) {
- T1::collect_completion_keywords(f);
- T2::collect_completion_keywords(f);
- }
-}
diff --git a/components/style_traits/values.rs b/components/style_traits/values.rs
deleted file mode 100644
index 14025023cb3..00000000000
--- a/components/style_traits/values.rs
+++ /dev/null
@@ -1,567 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
-
-//! Helper types and traits for the handling of CSS values.
-
-use app_units::Au;
-use cssparser::ToCss as CssparserToCss;
-use cssparser::{serialize_string, ParseError, Parser, Token, UnicodeRange};
-use servo_arc::Arc;
-use std::fmt::{self, Write};
-
-/// Serialises a value according to its CSS representation.
-///
-/// This trait is implemented for `str` and its friends, serialising the string
-/// contents as a CSS quoted string.
-///
-/// This trait is derivable with `#[derive(ToCss)]`, with the following behaviour:
-/// * unit variants get serialised as the `snake-case` representation
-/// of their name;
-/// * unit variants whose name starts with "Moz" or "Webkit" are prepended
-/// with a "-";
-/// * if `#[css(comma)]` is found on a variant, its fields are separated by
-/// commas, otherwise, by spaces;
-/// * if `#[css(function)]` is found on a variant, the variant name gets
-/// serialised like unit variants and its fields are surrounded by parentheses;
-/// * if `#[css(iterable)]` is found on a function variant, that variant needs
-/// to have a single member, and that member needs to be iterable. The
-/// iterable will be serialized as the arguments for the function;
-/// * an iterable field can also be annotated with `#[css(if_empty = "foo")]`
-/// to print `"foo"` if the iterator is empty;
-/// * if `#[css(dimension)]` is found on a variant, that variant needs
-/// to have a single member. The variant would be serialized as a CSS
-/// dimension token, like: <member><identifier>;
-/// * if `#[css(skip)]` is found on a field, the `ToCss` call for that field
-/// is skipped;
-/// * if `#[css(skip_if = "function")]` is found on a field, the `ToCss` call
-/// for that field is skipped if `function` returns true. This function is
-/// provided the field as an argument;
-/// * if `#[css(contextual_skip_if = "function")]` is found on a field, the
-/// `ToCss` call for that field is skipped if `function` returns true. This
-/// function is given all the fields in the current struct or variant as an
-/// argument;
-/// * `#[css(represents_keyword)]` can be used on bool fields in order to
-/// serialize the field name if the field is true, or nothing otherwise. It
-/// also collects those keywords for `SpecifiedValueInfo`.
-/// * `#[css(bitflags(single="", mixed="", validate="", overlapping_bits)]` can
-/// be used to derive parse / serialize / etc on bitflags. The rules for parsing
-/// bitflags are the following:
-///
-/// * `single` flags can only appear on their own. It's common that bitflags
-/// properties at least have one such value like `none` or `auto`.
-/// * `mixed` properties can appear mixed together, but not along any other
-/// flag that shares a bit with itself. For example, if you have three
-/// bitflags like:
-///
-/// FOO = 1 << 0;
-/// BAR = 1 << 1;
-/// BAZ = 1 << 2;
-/// BAZZ = BAR | BAZ;
-///
-/// Then the following combinations won't be valid:
-///
-/// * foo foo: (every flag shares a bit with itself)
-/// * bar bazz: (bazz shares a bit with bar)
-///
-/// But `bar baz` will be valid, as they don't share bits, and so would
-/// `foo` with any other flag, or `bazz` on its own.
-/// * `overlapping_bits` enables some tracking during serialization of mixed
-/// flags to avoid serializing variants that can subsume other variants.
-/// In the example above, you could do:
-/// mixed="foo,bazz,bar,baz", overlapping_bits
-/// to ensure that if bazz is serialized, bar and baz aren't, even though
-/// their bits are set. Note that the serialization order is canonical,
-/// and thus depends on the order you specify the flags in.
-///
-/// * finally, one can put `#[css(derive_debug)]` on the whole type, to
-/// implement `Debug` by a single call to `ToCss::to_css`.
-pub trait ToCss {
- /// Serialize `self` in CSS syntax, writing to `dest`.
- fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
- where
- W: Write;
-
- /// Serialize `self` in CSS syntax and return a string.
- ///
- /// (This is a convenience wrapper for `to_css` and probably should not be overridden.)
- #[inline]
- fn to_css_string(&self) -> String {
- let mut s = String::new();
- self.to_css(&mut CssWriter::new(&mut s)).unwrap();
- s
- }
-}
-
-impl<'a, T> ToCss for &'a T
-where
- T: ToCss + ?Sized,
-{
- fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
- where
- W: Write,
- {
- (*self).to_css(dest)
- }
-}
-
-impl ToCss for crate::owned_str::OwnedStr {
- #[inline]
- fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
- where
- W: Write,
- {
- serialize_string(self, dest)
- }
-}
-
-impl ToCss for str {
- #[inline]
- fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
- where
- W: Write,
- {
- serialize_string(self, dest)
- }
-}
-
-impl ToCss for String {
- #[inline]
- fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
- where
- W: Write,
- {
- serialize_string(self, dest)
- }
-}
-
-impl<T> ToCss for Option<T>
-where
- T: ToCss,
-{
- #[inline]
- fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
- where
- W: Write,
- {
- self.as_ref().map_or(Ok(()), |value| value.to_css(dest))
- }
-}
-
-impl ToCss for () {
- #[inline]
- fn to_css<W>(&self, _: &mut CssWriter<W>) -> fmt::Result
- where
- W: Write,
- {
- Ok(())
- }
-}
-
-/// A writer tailored for serialising CSS.
-///
-/// Coupled with SequenceWriter, this allows callers to transparently handle
-/// things like comma-separated values etc.
-pub struct CssWriter<'w, W: 'w> {
- inner: &'w mut W,
- prefix: Option<&'static str>,
-}
-
-impl<'w, W> CssWriter<'w, W>
-where
- W: Write,
-{
- /// Creates a new `CssWriter`.
- #[inline]
- pub fn new(inner: &'w mut W) -> Self {
- Self {
- inner,
- prefix: Some(""),
- }
- }
-}
-
-impl<'w, W> Write for CssWriter<'w, W>
-where
- W: Write,
-{
- #[inline]
- fn write_str(&mut self, s: &str) -> fmt::Result {
- if s.is_empty() {
- return Ok(());
- }
- if let Some(prefix) = self.prefix.take() {
- // We are going to write things, but first we need to write
- // the prefix that was set by `SequenceWriter::item`.
- if !prefix.is_empty() {
- self.inner.write_str(prefix)?;
- }
- }
- self.inner.write_str(s)
- }
-
- #[inline]
- fn write_char(&mut self, c: char) -> fmt::Result {
- if let Some(prefix) = self.prefix.take() {
- // See comment in `write_str`.
- if !prefix.is_empty() {
- self.inner.write_str(prefix)?;
- }
- }
- self.inner.write_char(c)
- }
-}
-
-/// Convenience wrapper to serialise CSS values separated by a given string.
-pub struct SequenceWriter<'a, 'b: 'a, W: 'b> {
- inner: &'a mut CssWriter<'b, W>,
- separator: &'static str,
-}
-
-impl<'a, 'b, W> SequenceWriter<'a, 'b, W>
-where
- W: Write + 'b,
-{
- /// Create a new sequence writer.
- #[inline]
- pub fn new(inner: &'a mut CssWriter<'b, W>, separator: &'static str) -> Self {
- if inner.prefix.is_none() {
- // See comment in `item`.
- inner.prefix = Some("");
- }
- Self { inner, separator }
- }
-
- #[inline]
- fn write_item<F>(&mut self, f: F) -> fmt::Result
- where
- F: FnOnce(&mut CssWriter<'b, W>) -> fmt::Result,
- {
- // Separate non-generic functions so that this code is not repeated
- // in every monomorphization with a different type `F` or `W`.
- // https://github.com/servo/servo/issues/26713
- fn before(
- prefix: &mut Option<&'static str>,
- separator: &'static str,
- ) -> Option<&'static str> {
- let old_prefix = *prefix;
- if old_prefix.is_none() {
- // If there is no prefix in the inner writer, a previous
- // call to this method produced output, which means we need
- // to write the separator next time we produce output again.
- *prefix = Some(separator);
- }
- old_prefix
- }
- fn after(
- old_prefix: Option<&'static str>,
- prefix: &mut Option<&'static str>,
- separator: &'static str,
- ) {
- match (old_prefix, *prefix) {
- (_, None) => {
- // This call produced output and cleaned up after itself.
- },
- (None, Some(p)) => {
- // Some previous call to `item` produced output,
- // but this one did not, prefix should be the same as
- // the one we set.
- debug_assert_eq!(separator, p);
- // We clean up here even though it's not necessary just
- // to be able to do all these assertion checks.
- *prefix = None;
- },
- (Some(old), Some(new)) => {
- // No previous call to `item` produced output, and this one
- // either.
- debug_assert_eq!(old, new);
- },
- }
- }
-
- let old_prefix = before(&mut self.inner.prefix, self.separator);
- f(self.inner)?;
- after(old_prefix, &mut self.inner.prefix, self.separator);
- Ok(())
- }
-
- /// Serialises a CSS value, writing any separator as necessary.
- ///
- /// The separator is never written before any `item` produces any output,
- /// and is written in subsequent calls only if the `item` produces some
- /// output on its own again. This lets us handle `Option<T>` fields by
- /// just not printing anything on `None`.
- #[inline]
- pub fn item<T>(&mut self, item: &T) -> fmt::Result
- where
- T: ToCss,
- {
- self.write_item(|inner| item.to_css(inner))
- }
-
- /// Writes a string as-is (i.e. not escaped or wrapped in quotes)
- /// with any separator as necessary.
- ///
- /// See SequenceWriter::item.
- #[inline]
- pub fn raw_item(&mut self, item: &str) -> fmt::Result {
- self.write_item(|inner| inner.write_str(item))
- }
-}
-
-/// Type used as the associated type in the `OneOrMoreSeparated` trait on a
-/// type to indicate that a serialized list of elements of this type is
-/// separated by commas.
-pub struct Comma;
-
-/// Type used as the associated type in the `OneOrMoreSeparated` trait on a
-/// type to indicate that a serialized list of elements of this type is
-/// separated by spaces.
-pub struct Space;
-
-/// Type used as the associated type in the `OneOrMoreSeparated` trait on a
-/// type to indicate that a serialized list of elements of this type is
-/// separated by commas, but spaces without commas are also allowed when
-/// parsing.
-pub struct CommaWithSpace;
-
-/// A trait satisfied by the types corresponding to separators.
-pub trait Separator {
- /// The separator string that the satisfying separator type corresponds to.
- fn separator() -> &'static str;
-
- /// Parses a sequence of values separated by this separator.
- ///
- /// The given closure is called repeatedly for each item in the sequence.
- ///
- /// Successful results are accumulated in a vector.
- ///
- /// This method returns `Err(_)` the first time a closure does or if
- /// the separators aren't correct.
- fn parse<'i, 't, F, T, E>(
- parser: &mut Parser<'i, 't>,
- parse_one: F,
- ) -> Result<Vec<T>, ParseError<'i, E>>
- where
- F: for<'tt> FnMut(&mut Parser<'i, 'tt>) -> Result<T, ParseError<'i, E>>;
-}
-
-impl Separator for Comma {
- fn separator() -> &'static str {
- ", "
- }
-
- fn parse<'i, 't, F, T, E>(
- input: &mut Parser<'i, 't>,
- parse_one: F,
- ) -> Result<Vec<T>, ParseError<'i, E>>
- where
- F: for<'tt> FnMut(&mut Parser<'i, 'tt>) -> Result<T, ParseError<'i, E>>,
- {
- input.parse_comma_separated(parse_one)
- }
-}
-
-impl Separator for Space {
- fn separator() -> &'static str {
- " "
- }
-
- fn parse<'i, 't, F, T, E>(
- input: &mut Parser<'i, 't>,
- mut parse_one: F,
- ) -> Result<Vec<T>, ParseError<'i, E>>
- where
- F: for<'tt> FnMut(&mut Parser<'i, 'tt>) -> Result<T, ParseError<'i, E>>,
- {
- input.skip_whitespace(); // Unnecessary for correctness, but may help try() rewind less.
- let mut results = vec![parse_one(input)?];
- loop {
- input.skip_whitespace(); // Unnecessary for correctness, but may help try() rewind less.
- if let Ok(item) = input.try(&mut parse_one) {
- results.push(item);
- } else {
- return Ok(results);
- }
- }
- }
-}
-
-impl Separator for CommaWithSpace {
- fn separator() -> &'static str {
- ", "
- }
-
- fn parse<'i, 't, F, T, E>(
- input: &mut Parser<'i, 't>,
- mut parse_one: F,
- ) -> Result<Vec<T>, ParseError<'i, E>>
- where
- F: for<'tt> FnMut(&mut Parser<'i, 'tt>) -> Result<T, ParseError<'i, E>>,
- {
- input.skip_whitespace(); // Unnecessary for correctness, but may help try() rewind less.
- let mut results = vec![parse_one(input)?];
- loop {
- input.skip_whitespace(); // Unnecessary for correctness, but may help try() rewind less.
- let comma_location = input.current_source_location();
- let comma = input.try(|i| i.expect_comma()).is_ok();
- input.skip_whitespace(); // Unnecessary for correctness, but may help try() rewind less.
- if let Ok(item) = input.try(&mut parse_one) {
- results.push(item);
- } else if comma {
- return Err(comma_location.new_unexpected_token_error(Token::Comma));
- } else {
- break;
- }
- }
- Ok(results)
- }
-}
-
-/// Marker trait on T to automatically implement ToCss for Vec<T> when T's are
-/// separated by some delimiter `delim`.
-pub trait OneOrMoreSeparated {
- /// Associated type indicating which separator is used.
- type S: Separator;
-}
-
-impl OneOrMoreSeparated for UnicodeRange {
- type S = Comma;
-}
-
-impl<T> ToCss for Vec<T>
-where
- T: ToCss + OneOrMoreSeparated,
-{
- fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
- where
- W: Write,
- {
- let mut iter = self.iter();
- iter.next().unwrap().to_css(dest)?;
- for item in iter {
- dest.write_str(<T as OneOrMoreSeparated>::S::separator())?;
- item.to_css(dest)?;
- }
- Ok(())
- }
-}
-
-impl<T> ToCss for Box<T>
-where
- T: ?Sized + ToCss,
-{
- fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
- where
- W: Write,
- {
- (**self).to_css(dest)
- }
-}
-
-impl<T> ToCss for Arc<T>
-where
- T: ?Sized + ToCss,
-{
- fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
- where
- W: Write,
- {
- (**self).to_css(dest)
- }
-}
-
-impl ToCss for Au {
- fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
- where
- W: Write,
- {
- self.to_f64_px().to_css(dest)?;
- dest.write_str("px")
- }
-}
-
-macro_rules! impl_to_css_for_predefined_type {
- ($name: ty) => {
- impl<'a> ToCss for $name {
- fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
- where
- W: Write,
- {
- ::cssparser::ToCss::to_css(self, dest)
- }
- }
- };
-}
-
-impl_to_css_for_predefined_type!(f32);
-impl_to_css_for_predefined_type!(i8);
-impl_to_css_for_predefined_type!(i32);
-impl_to_css_for_predefined_type!(u16);
-impl_to_css_for_predefined_type!(u32);
-impl_to_css_for_predefined_type!(::cssparser::Token<'a>);
-impl_to_css_for_predefined_type!(::cssparser::UnicodeRange);
-
-/// Helper types for the handling of specified values.
-pub mod specified {
- use crate::ParsingMode;
-
- /// Whether to allow negative lengths or not.
- #[repr(u8)]
- #[derive(
- Clone,
- Copy,
- Debug,
- Deserialize,
- Eq,
- MallocSizeOf,
- PartialEq,
- PartialOrd,
- Serialize,
- to_shmem_derive::ToShmem,
- )]
- pub enum AllowedNumericType {
- /// Allow all kind of numeric values.
- All,
- /// Allow only non-negative numeric values.
- NonNegative,
- /// Allow only numeric values greater or equal to 1.0.
- AtLeastOne,
- /// Allow only numeric values from 0 to 1.0.
- ZeroToOne,
- }
-
- impl Default for AllowedNumericType {
- #[inline]
- fn default() -> Self {
- AllowedNumericType::All
- }
- }
-
- impl AllowedNumericType {
- /// Whether the value fits the rules of this numeric type.
- #[inline]
- pub fn is_ok(&self, parsing_mode: ParsingMode, val: f32) -> bool {
- if parsing_mode.allows_all_numeric_values() {
- return true;
- }
- match *self {
- AllowedNumericType::All => true,
- AllowedNumericType::NonNegative => val >= 0.0,
- AllowedNumericType::AtLeastOne => val >= 1.0,
- AllowedNumericType::ZeroToOne => val >= 0.0 && val <= 1.0,
- }
- }
-
- /// Clamp the value following the rules of this numeric type.
- #[inline]
- pub fn clamp(&self, val: f32) -> f32 {
- match *self {
- AllowedNumericType::All => val,
- AllowedNumericType::NonNegative => val.max(0.),
- AllowedNumericType::AtLeastOne => val.max(1.),
- AllowedNumericType::ZeroToOne => val.max(0.).min(1.),
- }
- }
- }
-}
diff --git a/components/to_shmem/Cargo.toml b/components/to_shmem/Cargo.toml
deleted file mode 100644
index 5072f102dd4..00000000000
--- a/components/to_shmem/Cargo.toml
+++ /dev/null
@@ -1,22 +0,0 @@
-[package]
-name = "to_shmem"
-version = "0.0.1"
-authors = ["The Servo Project Developers"]
-license = "MPL-2.0"
-publish = false
-
-[lib]
-name = "to_shmem"
-path = "lib.rs"
-
-[features]
-servo = ["cssparser/serde", "string_cache"]
-gecko = []
-
-[dependencies]
-cssparser = { workspace = true }
-servo_arc = { path = "../servo_arc" }
-smallbitvec = { workspace = true }
-smallvec = { workspace = true }
-string_cache = { workspace = true, optional = true }
-thin-vec = { workspace = true }
diff --git a/components/to_shmem/lib.rs b/components/to_shmem/lib.rs
deleted file mode 100644
index 0a708999d1d..00000000000
--- a/components/to_shmem/lib.rs
+++ /dev/null
@@ -1,596 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
-
-//! Trait for cloning data into a shared memory buffer.
-//!
-//! This module contains the SharedMemoryBuilder type and ToShmem trait.
-//!
-//! We put them here (and not in style_traits) so that we can derive ToShmem
-//! from the selectors and style crates.
-
-#![crate_name = "to_shmem"]
-#![crate_type = "rlib"]
-
-extern crate cssparser;
-extern crate servo_arc;
-extern crate smallbitvec;
-extern crate smallvec;
-#[cfg(feature = "string_cache")]
-extern crate string_cache;
-extern crate thin_vec;
-
-use servo_arc::{Arc, ThinArc};
-use smallbitvec::{InternalStorage, SmallBitVec};
-use smallvec::{Array, SmallVec};
-use std::alloc::Layout;
-use std::collections::HashSet;
-use std::ffi::CString;
-use std::isize;
-use std::marker::PhantomData;
-use std::mem::{self, ManuallyDrop};
-use std::num::Wrapping;
-use std::ops::Range;
-use std::os::raw::c_char;
-#[cfg(debug_assertions)]
-use std::os::raw::c_void;
-use std::ptr::{self, NonNull};
-use std::slice;
-use std::str;
-use thin_vec::ThinVec;
-
-/// Result type for ToShmem::to_shmem.
-///
-/// The String is an error message describing why the call failed.
-pub type Result<T> = std::result::Result<ManuallyDrop<T>, String>;
-
-// Various pointer arithmetic functions in this file can be replaced with
-// functions on `Layout` once they have stabilized:
-//
-// https://github.com/rust-lang/rust/issues/55724
-
-/// A builder object that transforms and copies values into a fixed size buffer.
-pub struct SharedMemoryBuilder {
- /// The buffer into which values will be copied.
- buffer: *mut u8,
- /// The size of the buffer.
- capacity: usize,
- /// The current position in the buffer, where the next value will be written
- /// at.
- index: usize,
- /// Pointers to every shareable value that we store in the shared memory
- /// buffer. We use this to assert against encountering the same value
- /// twice, e.g. through another Arc reference, so that we don't
- /// inadvertently store duplicate copies of values.
- #[cfg(debug_assertions)]
- shared_values: HashSet<*const c_void>,
-}
-
-/// Amount of padding needed after `size` bytes to ensure that the following
-/// address will satisfy `align`.
-fn padding_needed_for(size: usize, align: usize) -> usize {
- padded_size(size, align).wrapping_sub(size)
-}
-
-/// Rounds up `size` so that the following address will satisfy `align`.
-fn padded_size(size: usize, align: usize) -> usize {
- size.wrapping_add(align).wrapping_sub(1) & !align.wrapping_sub(1)
-}
-
-impl SharedMemoryBuilder {
- /// Creates a new SharedMemoryBuilder using the specified buffer.
- pub unsafe fn new(buffer: *mut u8, capacity: usize) -> SharedMemoryBuilder {
- SharedMemoryBuilder {
- buffer,
- capacity,
- index: 0,
- #[cfg(debug_assertions)]
- shared_values: HashSet::new(),
- }
- }
-
- /// Returns the number of bytes currently used in the buffer.
- #[inline]
- pub fn len(&self) -> usize {
- self.index
- }
-
- /// Writes a value into the shared memory buffer and returns a pointer to
- /// it in the buffer.
- ///
- /// The value is cloned and converted into a form suitable for placing into
- /// a shared memory buffer by calling ToShmem::to_shmem on it.
- ///
- /// Panics if there is insufficient space in the buffer.
- pub fn write<T: ToShmem>(&mut self, value: &T) -> std::result::Result<*mut T, String> {
- // Reserve space for the value.
- let dest: *mut T = self.alloc_value();
-
- // Make a clone of the value with all of its heap allocations
- // placed in the shared memory buffer.
- let value = value.to_shmem(self)?;
-
- unsafe {
- // Copy the value into the buffer.
- ptr::write(dest, ManuallyDrop::into_inner(value));
- }
-
- // Return a pointer to the shared value.
- Ok(dest)
- }
-
- /// Reserves space in the shared memory buffer to fit a value of type T,
- /// and returns a pointer to that reserved space.
- ///
- /// Panics if there is insufficient space in the buffer.
- pub fn alloc_value<T>(&mut self) -> *mut T {
- self.alloc(Layout::new::<T>())
- }
-
- /// Reserves space in the shared memory buffer to fit an array of values of
- /// type T, and returns a pointer to that reserved space.
- ///
- /// Panics if there is insufficient space in the buffer.
- pub fn alloc_array<T>(&mut self, len: usize) -> *mut T {
- if len == 0 {
- return NonNull::dangling().as_ptr();
- }
-
- let size = mem::size_of::<T>();
- let align = mem::align_of::<T>();
-
- self.alloc(Layout::from_size_align(padded_size(size, align) * len, align).unwrap())
- }
-
- /// Reserves space in the shared memory buffer that conforms to the
- /// specified layout, and returns a pointer to that reserved space.
- ///
- /// Panics if there is insufficient space in the buffer.
- pub fn alloc<T>(&mut self, layout: Layout) -> *mut T {
- // Amount of padding to align the value.
- //
- // The addition can't overflow, since self.index <= self.capacity, and
- // for us to have successfully allocated the buffer, `buffer + capacity`
- // can't overflow.
- let padding = padding_needed_for(self.buffer as usize + self.index, layout.align());
-
- // Reserve space for the padding.
- let start = self.index.checked_add(padding).unwrap();
- assert!(start <= std::isize::MAX as usize); // for the cast below
-
- // Reserve space for the value.
- let end = start.checked_add(layout.size()).unwrap();
- assert!(end <= self.capacity);
-
- self.index = end;
- unsafe { self.buffer.add(start) as *mut T }
- }
-}
-
-/// A type that can be copied into a SharedMemoryBuilder.
-pub trait ToShmem: Sized {
- /// Clones this value into a form suitable for writing into a
- /// SharedMemoryBuilder.
- ///
- /// If this value owns any heap allocations, they should be written into
- /// `builder` so that the return value of this function can point to the
- /// copy in the shared memory buffer.
- ///
- /// The return type is wrapped in ManuallyDrop to make it harder to
- /// accidentally invoke the destructor of the value that is produced.
- ///
- /// Returns a Result so that we can gracefully recover from unexpected
- /// content.
- fn to_shmem(&self, builder: &mut SharedMemoryBuilder) -> Result<Self>;
-}
-
-#[macro_export]
-macro_rules! impl_trivial_to_shmem {
- ($($ty:ty),*) => {
- $(
- impl $crate::ToShmem for $ty {
- fn to_shmem(
- &self,
- _builder: &mut $crate::SharedMemoryBuilder,
- ) -> $crate::Result<Self> {
- $crate::Result::Ok(::std::mem::ManuallyDrop::new(*self))
- }
- }
- )*
- };
-}
-
-impl_trivial_to_shmem!(
- (),
- bool,
- f32,
- f64,
- i8,
- i16,
- i32,
- i64,
- u8,
- u16,
- u32,
- u64,
- isize,
- usize
-);
-
-impl_trivial_to_shmem!(cssparser::SourceLocation);
-impl_trivial_to_shmem!(cssparser::TokenSerializationType);
-
-impl<T> ToShmem for PhantomData<T> {
- fn to_shmem(&self, _builder: &mut SharedMemoryBuilder) -> Result<Self> {
- Ok(ManuallyDrop::new(*self))
- }
-}
-
-impl<T: ToShmem> ToShmem for Range<T> {
- fn to_shmem(&self, builder: &mut SharedMemoryBuilder) -> Result<Self> {
- Ok(ManuallyDrop::new(Range {
- start: ManuallyDrop::into_inner(self.start.to_shmem(builder)?),
- end: ManuallyDrop::into_inner(self.end.to_shmem(builder)?),
- }))
- }
-}
-
-impl ToShmem for cssparser::UnicodeRange {
- fn to_shmem(&self, _builder: &mut SharedMemoryBuilder) -> Result<Self> {
- Ok(ManuallyDrop::new(cssparser::UnicodeRange {
- start: self.start,
- end: self.end,
- }))
- }
-}
-
-impl<T: ToShmem, U: ToShmem> ToShmem for (T, U) {
- fn to_shmem(&self, builder: &mut SharedMemoryBuilder) -> Result<Self> {
- Ok(ManuallyDrop::new((
- ManuallyDrop::into_inner(self.0.to_shmem(builder)?),
- ManuallyDrop::into_inner(self.1.to_shmem(builder)?),
- )))
- }
-}
-
-impl<T: ToShmem> ToShmem for Wrapping<T> {
- fn to_shmem(&self, builder: &mut SharedMemoryBuilder) -> Result<Self> {
- Ok(ManuallyDrop::new(Wrapping(ManuallyDrop::into_inner(
- self.0.to_shmem(builder)?,
- ))))
- }
-}
-
-impl<T: ToShmem> ToShmem for Box<T> {
- fn to_shmem(&self, builder: &mut SharedMemoryBuilder) -> Result<Self> {
- // Reserve space for the boxed value.
- let dest: *mut T = builder.alloc_value();
-
- // Make a clone of the boxed value with all of its heap allocations
- // placed in the shared memory buffer.
- let value = (**self).to_shmem(builder)?;
-
- unsafe {
- // Copy the value into the buffer.
- ptr::write(dest, ManuallyDrop::into_inner(value));
-
- Ok(ManuallyDrop::new(Box::from_raw(dest)))
- }
- }
-}
-
-/// Converts all the items in `src` into shared memory form, writes them into
-/// the specified buffer, and returns a pointer to the slice.
-unsafe fn to_shmem_slice_ptr<'a, T, I>(
- src: I,
- dest: *mut T,
- builder: &mut SharedMemoryBuilder,
-) -> std::result::Result<*mut [T], String>
-where
- T: 'a + ToShmem,
- I: ExactSizeIterator<Item = &'a T>,
-{
- let dest = slice::from_raw_parts_mut(dest, src.len());
-
- // Make a clone of each element from the iterator with its own heap
- // allocations placed in the buffer, and copy that clone into the buffer.
- for (src, dest) in src.zip(dest.iter_mut()) {
- ptr::write(dest, ManuallyDrop::into_inner(src.to_shmem(builder)?));
- }
-
- Ok(dest)
-}
-
-/// Writes all the items in `src` into a slice in the shared memory buffer and
-/// returns a pointer to the slice.
-pub unsafe fn to_shmem_slice<'a, T, I>(
- src: I,
- builder: &mut SharedMemoryBuilder,
-) -> std::result::Result<*mut [T], String>
-where
- T: 'a + ToShmem,
- I: ExactSizeIterator<Item = &'a T>,
-{
- let dest = builder.alloc_array(src.len());
- to_shmem_slice_ptr(src, dest, builder)
-}
-
-impl<T: ToShmem> ToShmem for Box<[T]> {
- fn to_shmem(&self, builder: &mut SharedMemoryBuilder) -> Result<Self> {
- unsafe {
- let dest = to_shmem_slice(self.iter(), builder)?;
- Ok(ManuallyDrop::new(Box::from_raw(dest)))
- }
- }
-}
-
-impl ToShmem for Box<str> {
- fn to_shmem(&self, builder: &mut SharedMemoryBuilder) -> Result<Self> {
- // Reserve space for the string bytes.
- let dest: *mut u8 = builder.alloc_array(self.len());
-
- unsafe {
- // Copy the value into the buffer.
- ptr::copy(self.as_ptr(), dest, self.len());
-
- Ok(ManuallyDrop::new(Box::from_raw(
- str::from_utf8_unchecked_mut(slice::from_raw_parts_mut(dest, self.len())),
- )))
- }
- }
-}
-
-impl ToShmem for String {
- fn to_shmem(&self, builder: &mut SharedMemoryBuilder) -> Result<Self> {
- // Reserve space for the string bytes.
- let dest: *mut u8 = builder.alloc_array(self.len());
-
- unsafe {
- // Copy the value into the buffer.
- ptr::copy(self.as_ptr(), dest, self.len());
-
- Ok(ManuallyDrop::new(String::from_raw_parts(
- dest,
- self.len(),
- self.len(),
- )))
- }
- }
-}
-
-impl ToShmem for CString {
- fn to_shmem(&self, builder: &mut SharedMemoryBuilder) -> Result<Self> {
- let len = self.as_bytes_with_nul().len();
-
- // Reserve space for the string bytes.
- let dest: *mut c_char = builder.alloc_array(len);
-
- unsafe {
- // Copy the value into the buffer.
- ptr::copy(self.as_ptr(), dest, len);
-
- Ok(ManuallyDrop::new(CString::from_raw(dest)))
- }
- }
-}
-
-impl<T: ToShmem> ToShmem for Vec<T> {
- fn to_shmem(&self, builder: &mut SharedMemoryBuilder) -> Result<Self> {
- unsafe {
- let dest = to_shmem_slice(self.iter(), builder)? as *mut T;
- let dest_vec = Vec::from_raw_parts(dest, self.len(), self.len());
- Ok(ManuallyDrop::new(dest_vec))
- }
- }
-}
-
-impl<T: ToShmem, A: Array<Item = T>> ToShmem for SmallVec<A> {
- fn to_shmem(&self, builder: &mut SharedMemoryBuilder) -> Result<Self> {
- let dest_vec = unsafe {
- if self.spilled() {
- // Place the items in a separate allocation in the shared memory
- // buffer.
- let dest = to_shmem_slice(self.iter(), builder)? as *mut T;
- SmallVec::from_raw_parts(dest, self.len(), self.len())
- } else {
- // Place the items inline.
- let mut s = SmallVec::new();
- to_shmem_slice_ptr(self.iter(), s.as_mut_ptr(), builder)?;
- s.set_len(self.len());
- s
- }
- };
-
- Ok(ManuallyDrop::new(dest_vec))
- }
-}
-
-impl<T: ToShmem> ToShmem for Option<T> {
- fn to_shmem(&self, builder: &mut SharedMemoryBuilder) -> Result<Self> {
- let v = match self {
- Some(v) => Some(ManuallyDrop::into_inner(v.to_shmem(builder)?)),
- None => None,
- };
-
- Ok(ManuallyDrop::new(v))
- }
-}
-
-impl<T: ToShmem, S> ToShmem for HashSet<T, S>
-where
- Self: Default,
-{
- fn to_shmem(&self, _builder: &mut SharedMemoryBuilder) -> Result<Self> {
- if !self.is_empty() {
- return Err(format!(
- "ToShmem failed for HashSet: We only support empty sets \
- (we don't expect custom properties in UA sheets, they're observable by content)",
- ));
- }
- Ok(ManuallyDrop::new(Self::default()))
- }
-}
-
-impl<T: ToShmem> ToShmem for Arc<T> {
- fn to_shmem(&self, builder: &mut SharedMemoryBuilder) -> Result<Self> {
- // Assert that we don't encounter any shared references to values we
- // don't expect.
- #[cfg(debug_assertions)]
- assert!(
- !builder.shared_values.contains(&self.heap_ptr()),
- "ToShmem failed for Arc<{}>: encountered a value with multiple \
- references.",
- std::any::type_name::<T>()
- );
-
- // Make a clone of the Arc-owned value with all of its heap allocations
- // placed in the shared memory buffer.
- let value = (**self).to_shmem(builder)?;
-
- // Create a new Arc with the shared value and have it place its
- // ArcInner in the shared memory buffer.
- unsafe {
- let static_arc = Arc::new_static(
- |layout| builder.alloc(layout),
- ManuallyDrop::into_inner(value),
- );
-
- #[cfg(debug_assertions)]
- builder.shared_values.insert(self.heap_ptr());
-
- Ok(ManuallyDrop::new(static_arc))
- }
- }
-}
-
-impl<H: ToShmem, T: ToShmem> ToShmem for ThinArc<H, T> {
- fn to_shmem(&self, builder: &mut SharedMemoryBuilder) -> Result<Self> {
- // We don't currently have any shared ThinArc values in stylesheets,
- // so don't support them for now.
- #[cfg(debug_assertions)]
- assert!(
- !builder.shared_values.contains(&self.heap_ptr()),
- "ToShmem failed for ThinArc<T>: encountered a value with multiple references, which \
- is not currently supported",
- );
-
- // Make a clone of the Arc-owned header and slice values with all of
- // their heap allocations placed in the shared memory buffer.
- let header = self.header.header.to_shmem(builder)?;
- let mut values = Vec::with_capacity(self.slice.len());
- for v in self.slice.iter() {
- values.push(v.to_shmem(builder)?);
- }
-
- // Create a new ThinArc with the shared value and have it place
- // its ArcInner in the shared memory buffer.
- unsafe {
- let static_arc = ThinArc::static_from_header_and_iter(
- |layout| builder.alloc(layout),
- ManuallyDrop::into_inner(header),
- values.into_iter().map(ManuallyDrop::into_inner),
- );
-
- #[cfg(debug_assertions)]
- builder.shared_values.insert(self.heap_ptr());
-
- Ok(ManuallyDrop::new(static_arc))
- }
- }
-}
-
-impl<T: ToShmem> ToShmem for ThinVec<T> {
- fn to_shmem(&self, builder: &mut SharedMemoryBuilder) -> Result<Self> {
- let len = self.len();
- if len == 0 {
- return Ok(ManuallyDrop::new(Self::new()));
- }
-
- assert_eq!(mem::size_of::<Self>(), mem::size_of::<*const ()>());
-
- // nsTArrayHeader size.
- // FIXME: Would be nice not to hard-code this, but in practice thin-vec crate also relies
- // on this.
- let header_size = 2 * mem::size_of::<u32>();
- let header_align = mem::size_of::<u32>();
-
- let item_size = mem::size_of::<T>();
- let item_align = mem::align_of::<T>();
-
- // We don't need to support underalignment for now, this could be supported if needed.
- assert!(item_align >= header_align);
-
- // This is explicitly unsupported by ThinVec, see:
- // https://searchfox.org/mozilla-central/rev/ad732108b073742d7324f998c085f459674a6846/third_party/rust/thin-vec/src/lib.rs#375-386
- assert!(item_align <= header_size);
- let header_padding = 0;
-
- let layout = Layout::from_size_align(
- header_size + header_padding + padded_size(item_size, item_align) * len,
- item_align,
- )
- .unwrap();
-
- let shmem_header_ptr = builder.alloc::<u8>(layout);
- let shmem_data_ptr = unsafe { shmem_header_ptr.add(header_size + header_padding) };
-
- let data_ptr = self.as_ptr() as *const T as *const u8;
- let header_ptr = unsafe { data_ptr.sub(header_size + header_padding) };
-
- unsafe {
- // Copy the header. Note this might copy a wrong capacity, but it doesn't matter,
- // because shared memory ptrs are immutable anyways, and we can't relocate.
- ptr::copy(header_ptr, shmem_header_ptr, header_size);
- // ToShmem + copy the contents into the shared buffer.
- to_shmem_slice_ptr(self.iter(), shmem_data_ptr as *mut T, builder)?;
- // Return the new ThinVec, which is just a pointer to the shared memory buffer.
- let shmem_thinvec: Self = mem::transmute(shmem_header_ptr);
-
- // Sanity-check that the ptr and length match.
- debug_assert_eq!(shmem_thinvec.as_ptr(), shmem_data_ptr as *const T);
- debug_assert_eq!(shmem_thinvec.len(), len);
-
- Ok(ManuallyDrop::new(shmem_thinvec))
- }
- }
-}
-
-impl ToShmem for SmallBitVec {
- fn to_shmem(&self, builder: &mut SharedMemoryBuilder) -> Result<Self> {
- let storage = match self.clone().into_storage() {
- InternalStorage::Spilled(vs) => {
- // Reserve space for the boxed slice values.
- let len = vs.len();
- let dest: *mut usize = builder.alloc_array(len);
-
- unsafe {
- // Copy the value into the buffer.
- let src = vs.as_ptr() as *const usize;
- ptr::copy(src, dest, len);
-
- let dest_slice =
- Box::from_raw(slice::from_raw_parts_mut(dest, len) as *mut [usize]);
- InternalStorage::Spilled(dest_slice)
- }
- },
- InternalStorage::Inline(x) => InternalStorage::Inline(x),
- };
- Ok(ManuallyDrop::new(unsafe {
- SmallBitVec::from_storage(storage)
- }))
- }
-}
-
-#[cfg(feature = "string_cache")]
-impl<Static: string_cache::StaticAtomSet> ToShmem for string_cache::Atom<Static> {
- fn to_shmem(&self, _: &mut SharedMemoryBuilder) -> Result<Self> {
- // NOTE(emilio): In practice, this can be implemented trivially if
- // string_cache could expose the implementation detail of static atoms
- // being an index into the static table (and panicking in the
- // non-static, non-inline cases).
- unimplemented!(
- "If servo wants to share stylesheets across processes, \
- then ToShmem for Atom needs to be implemented"
- )
- }
-}
diff --git a/components/to_shmem/rustfmt.toml b/components/to_shmem/rustfmt.toml
deleted file mode 100644
index c7ad93bafe3..00000000000
--- a/components/to_shmem/rustfmt.toml
+++ /dev/null
@@ -1 +0,0 @@
-disable_all_formatting = true
diff --git a/components/to_shmem_derive/Cargo.toml b/components/to_shmem_derive/Cargo.toml
deleted file mode 100644
index 2aa8b6b145f..00000000000
--- a/components/to_shmem_derive/Cargo.toml
+++ /dev/null
@@ -1,18 +0,0 @@
-[package]
-name = "to_shmem_derive"
-version = "0.0.1"
-authors = ["The Servo Project Developers"]
-license = "MPL-2.0"
-publish = false
-
-[lib]
-path = "lib.rs"
-proc-macro = true
-
-[dependencies]
-darling = { workspace = true }
-derive_common = { path = "../derive_common" }
-proc-macro2 = { workspace = true }
-quote = { workspace = true }
-syn = { workspace = true }
-synstructure = { workspace = true }
diff --git a/components/to_shmem_derive/lib.rs b/components/to_shmem_derive/lib.rs
deleted file mode 100644
index b820e7f85d0..00000000000
--- a/components/to_shmem_derive/lib.rs
+++ /dev/null
@@ -1,26 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
-
-#![recursion_limit = "128"]
-
-#[macro_use]
-extern crate darling;
-extern crate derive_common;
-extern crate proc_macro;
-extern crate proc_macro2;
-#[macro_use]
-extern crate quote;
-#[macro_use]
-extern crate syn;
-extern crate synstructure;
-
-use proc_macro::TokenStream;
-
-mod to_shmem;
-
-#[proc_macro_derive(ToShmem, attributes(shmem))]
-pub fn derive_to_shmem(stream: TokenStream) -> TokenStream {
- let input = syn::parse(stream).unwrap();
- to_shmem::derive(input).into()
-}
diff --git a/components/to_shmem_derive/to_shmem.rs b/components/to_shmem_derive/to_shmem.rs
deleted file mode 100644
index 157730c5a51..00000000000
--- a/components/to_shmem_derive/to_shmem.rs
+++ /dev/null
@@ -1,78 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
-
-use derive_common::cg;
-use proc_macro2::TokenStream;
-use syn;
-use synstructure::{BindStyle, Structure};
-
-pub fn derive(mut input: syn::DeriveInput) -> TokenStream {
- let mut where_clause = input.generics.where_clause.take();
- let attrs = cg::parse_input_attrs::<ShmemInputAttrs>(&input);
- if !attrs.no_bounds {
- for param in input.generics.type_params() {
- cg::add_predicate(&mut where_clause, parse_quote!(#param: ::to_shmem::ToShmem));
- }
- }
- for variant in Structure::new(&input).variants() {
- for binding in variant.bindings() {
- let attrs = cg::parse_field_attrs::<ShmemFieldAttrs>(&binding.ast());
- if attrs.field_bound {
- let ty = &binding.ast().ty;
- cg::add_predicate(&mut where_clause, parse_quote!(#ty: ::to_shmem::ToShmem))
- }
- }
- }
-
- input.generics.where_clause = where_clause;
-
- // Do all of the `to_shmem()?` calls before the `ManuallyDrop::into_inner()`
- // calls, so that we don't drop a value in the shared memory buffer if one
- // of the `to_shmem`s fails.
- let match_body = cg::fmap2_match(
- &input,
- BindStyle::Ref,
- |binding| {
- quote! {
- ::to_shmem::ToShmem::to_shmem(#binding, builder)?
- }
- },
- |binding| {
- Some(quote! {
- ::std::mem::ManuallyDrop::into_inner(#binding)
- })
- },
- );
-
- let name = &input.ident;
- let (impl_generics, ty_generics, where_clause) = input.generics.split_for_impl();
-
- quote! {
- impl #impl_generics ::to_shmem::ToShmem for #name #ty_generics #where_clause {
- #[allow(unused_variables, unreachable_code)]
- fn to_shmem(
- &self,
- builder: &mut ::to_shmem::SharedMemoryBuilder,
- ) -> ::to_shmem::Result<Self> {
- Ok(::std::mem::ManuallyDrop::new(
- match *self {
- #match_body
- }
- ))
- }
- }
- }
-}
-
-#[derive(Default, FromDeriveInput)]
-#[darling(attributes(shmem), default)]
-pub struct ShmemInputAttrs {
- pub no_bounds: bool,
-}
-
-#[derive(Default, FromField)]
-#[darling(attributes(shmem), default)]
-pub struct ShmemFieldAttrs {
- pub field_bound: bool,
-}
diff --git a/components/url/Cargo.toml b/components/url/Cargo.toml
index 69706b0e246..65c3fa75967 100644
--- a/components/url/Cargo.toml
+++ b/components/url/Cargo.toml
@@ -11,11 +11,11 @@ name = "servo_url"
path = "lib.rs"
[dependencies]
-malloc_size_of = { path = "../malloc_size_of", features = ["servo"] }
+malloc_size_of = { workspace = true }
malloc_size_of_derive = { workspace = true }
serde = { workspace = true, features = ["derive"] }
-servo_arc = { path = "../servo_arc" }
+servo_arc = { workspace = true }
servo_rand = { path = "../rand" }
-to_shmem = { path = "../to_shmem" }
+to_shmem = { workspace = true }
url = { workspace = true, features = ["serde"] }
uuid = { workspace = true, features = ["serde"] }
diff --git a/components/webgpu/Cargo.toml b/components/webgpu/Cargo.toml
index efc978f8580..4e70928c6e2 100644
--- a/components/webgpu/Cargo.toml
+++ b/components/webgpu/Cargo.toml
@@ -15,7 +15,7 @@ arrayvec = { workspace = true, features = ["serde"] }
euclid = { workspace = true }
ipc-channel = { workspace = true }
log = { workspace = true }
-malloc_size_of = { path = "../malloc_size_of" }
+malloc_size_of = { workspace = true }
msg = { workspace = true }
serde = { workspace = true, features = ["serde_derive"] }
servo_config = { path = "../config" }
diff --git a/etc/ci/generate_workflow.py b/etc/ci/generate_workflow.py
deleted file mode 100644
index a3295e1c0a2..00000000000
--- a/etc/ci/generate_workflow.py
+++ /dev/null
@@ -1,58 +0,0 @@
-# Copyright 2021 The Servo Project Developers. See the COPYRIGHT
-# file at the top-level directory of this distribution.
-#
-# Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
-# http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
-# <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
-# option. This file may not be copied, modified, or distributed
-# except according to those terms.
-
-import os.path
-import sys
-
-BASE = os.path.dirname(__file__.replace('\\', '/'))
-sys.path.insert(0, os.path.join(
- BASE, "..", "..", "components", "style", "properties", "Mako-1.1.2-py2.py3-none-any.whl"
-))
-
-from mako import exceptions # noqa
-from mako.lookup import TemplateLookup # noqa
-from mako.template import Template # noqa
-
-
-def abort(message):
- print(message, file=sys.stderr)
- sys.exit(1)
-
-
-def render(filename, **context):
- try:
- lookup = TemplateLookup(directories=[BASE],
- input_encoding="utf8",
- strict_undefined=True)
- template = Template(open(os.path.join(BASE, filename), "rb").read(),
- filename=filename,
- input_encoding="utf8",
- lookup=lookup,
- strict_undefined=True)
- # Uncomment to debug generated Python code:
- # write("/tmp", "mako_%s.py" % os.path.basename(filename), template.code)
- return template.render(**context)
- except Exception:
- # Uncomment to see a traceback in generated Python code:
- # raise
- abort(exceptions.text_error_template().render())
-
-
-def main():
- with open(os.path.join(".github", "workflows", "main.yml"), 'w') as f:
- f.write(render(
- 'workflow.mako',
- total_chunks=20,
- REPOSITORY_NAME="${{ github.event.repository.name }}",
- CODESIGN_CERT="${{ secrets.WINDOWS_CODESIGN_CERT }}",
- ))
-
-
-if __name__ == "__main__":
- main()
diff --git a/python/requirements.txt b/python/requirements.txt
index 7c56901599f..dc04dd0eae0 100644
--- a/python/requirements.txt
+++ b/python/requirements.txt
@@ -36,3 +36,6 @@ notify-py == 0.3.42
flask
requests
types-requests
+
+# For mach package on macOS.
+Mako == 1.1.2
diff --git a/python/servo/package_commands.py b/python/servo/package_commands.py
index ab8cbacb2cd..54afdd6a9e4 100644
--- a/python/servo/package_commands.py
+++ b/python/servo/package_commands.py
@@ -39,10 +39,6 @@ from servo.build_commands import copy_dependencies
from servo.gstreamer import macos_gst_root
from servo.util import delete, get_target_dir
-# Note: mako cannot be imported at the top level because it breaks mach bootstrap
-sys.path.append(path.join(path.dirname(__file__), "..", "..",
- "components", "style", "properties", "Mako-1.1.2-py2.py3-none-any.whl"))
-
PACKAGES = {
'android': [
'android/armv7-linux-androideabi/production/servoapp.apk',
diff --git a/servo-tidy.toml b/servo-tidy.toml
index 25d533e56b6..184616cb5ff 100644
--- a/servo-tidy.toml
+++ b/servo-tidy.toml
@@ -66,11 +66,6 @@ packages = [
# Files that are ignored for all tidy and lint checks.
files = [
"./components/net/tests/parsable_mime/text",
- # Ignore selectors and style files to avoid diverging too much from upstream Gecko
- "./components/selectors/",
- "./components/style/",
- "./components/style_derive/parse.rs",
- "./components/to_shmem/lib.rs",
"./resources/hsts_preload.json",
"./tests/wpt/meta/MANIFEST.json",
"./tests/wpt/meta-legacy-layout/MANIFEST.json",
@@ -90,8 +85,6 @@ files = [
"./tests/wpt/mozilla/tests/css/fonts",
"./tests/wpt/mozilla/tests/css/pre_with_tab.html",
"./tests/wpt/mozilla/tests/mozilla/textarea_placeholder.html",
- # Python 3 syntax causes "E901 SyntaxError" when flake8 runs in Python 2
- "./components/style/properties/build.py",
# The tidy tests currently don't pass tidy.
"./python/tidy/test.py",
]
diff --git a/tests/unit/malloc_size_of/Cargo.toml b/tests/unit/malloc_size_of/Cargo.toml
index 049c750972f..50051762a65 100644
--- a/tests/unit/malloc_size_of/Cargo.toml
+++ b/tests/unit/malloc_size_of/Cargo.toml
@@ -10,5 +10,5 @@ path = "lib.rs"
test = false
[dependencies]
-malloc_size_of = {path = "../../../components/malloc_size_of"}
-servo_arc = {path = "../../../components/servo_arc"}
+malloc_size_of = { workspace = true }
+servo_arc = { workspace = true }
diff --git a/tests/unit/style/Cargo.toml b/tests/unit/style/Cargo.toml
index ef02ffd00b3..d3d1bbd1c9d 100644
--- a/tests/unit/style/Cargo.toml
+++ b/tests/unit/style/Cargo.toml
@@ -16,9 +16,9 @@ euclid = { workspace = true }
html5ever = { workspace = true }
rayon = { workspace = true }
serde_json = { workspace = true }
-selectors = {path = "../../../components/selectors" }
-servo_arc = {path = "../../../components/servo_arc"}
-servo_atoms = {path = "../../../components/atoms"}
-style = {path = "../../../components/style", features = ["servo"]}
-style_traits = {path = "../../../components/style_traits"}
+selectors = { workspace = true }
+servo_arc = { workspace = true }
+servo_atoms = { workspace = true }
+style = { workspace = true }
+style_traits = { workspace = true }
url = { workspace = true }
diff --git a/components/style/stylesheets/font_face_rule.rs b/tests/unit/style/build.rs
index 78f3b338b22..8a25184fd27 100644
--- a/components/style/stylesheets/font_face_rule.rs
+++ b/tests/unit/style/build.rs
@@ -2,6 +2,7 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
-#![allow(missing_docs)]
-
-pub use crate::font_face::FontFaceRuleData as FontFaceRule;
+fn main() {
+ // Dummy build script, just so the code can get env!("OUT_DIR").
+ println!("cargo:rerun-if-changed=build.rs");
+}
diff --git a/tests/unit/style/properties/scaffolding.rs b/tests/unit/style/properties/scaffolding.rs
index e9f7148e2e5..fc2a9f1405d 100644
--- a/tests/unit/style/properties/scaffolding.rs
+++ b/tests/unit/style/properties/scaffolding.rs
@@ -10,14 +10,17 @@ use serde_json::{self, Value};
#[test]
fn properties_list_json() {
- let top = Path::new(&env::var("CARGO_MANIFEST_DIR").unwrap())
+ // Four dotdots: /path/to/target(4)/debug(3)/build(2)/style_tests-*(1)/out
+ // Do not ascend above the target dir, because it may not be called target
+ // or even have a parent (see CARGO_TARGET_DIR).
+ let target_dir = Path::new(env!("OUT_DIR"))
+ .join("..")
.join("..")
.join("..")
.join("..");
- let json = top
- .join("target")
+ let json = target_dir
.join("doc")
- .join("servo")
+ .join("stylo")
.join("css-properties.json");
let properties: Value = serde_json::from_reader(File::open(json).unwrap()).unwrap();