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