blitz-pdf 0.0.0-stage → 0.1.0

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