Coverage for /pythoncovmergedfiles/medio/medio/src/turbohtml/tools/fuzz/stylesheet_targets.py: 49%
Shortcuts on this page
r m x toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
Shortcuts on this page
r m x toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
1"""Whole stylesheet inputs must reach the public minifier without an HTML text boundary."""
3from __future__ import annotations
5from typing import TYPE_CHECKING, Final
7from turbohtml.clean import minify_css
9from .atheris_registry import Target
10from .round_trip_oracles import OutOfScopeError, css_semantics_check, fixpoint_check
12if TYPE_CHECKING:
13 from collections.abc import Callable
15__all__ = ["stylesheet_observation", "stylesheet_targets"]
18def stylesheet_targets() -> tuple[Target, ...]:
19 """Own the public minifier through an exact stylesheet input."""
20 return (
21 Target("css-stylesheet", _stylesheet, ("turbohtml.clean.minify_css",), (UnicodeDecodeError, OutOfScopeError)),
22 )
25def _stylesheet(data: bytes) -> None:
26 stylesheet_observation(data)
29def stylesheet_observation(data: bytes, printer: Callable[[str], str] = minify_css) -> str:
30 """Check supported values and selectors against a fixed DOM context."""
31 source: Final = data.decode("utf-8")
32 if _unclosed_css_blocks(source):
33 msg = "CSS input has an unclosed block"
34 raise OutOfScopeError(msg)
35 if failure := fixpoint_check(source, printer, numeric=True):
36 if _unclosed_css_blocks(printer(source)):
37 msg = "CSS output has an unclosed block"
38 raise OutOfScopeError(msg)
39 raise AssertionError(failure)
40 if failure := css_semantics_check(
41 '<style></style><p class="x">text<span id="target">child</span></p>',
42 printer,
43 stylesheet=source,
44 ):
45 raise AssertionError(failure)
46 return printer(source)
49def _unclosed_css_blocks(source: str) -> bool:
50 depth = 0
51 quote = ""
52 index = 0
53 while index < len(source):
54 char = source[index]
55 if quote:
56 if char == "\\":
57 index += 1
58 elif char == quote:
59 quote = ""
60 elif source.startswith("/*", index):
61 end = source.find("*/", index + 2)
62 index = len(source) if end < 0 else end + 2
63 continue
64 elif char in "\"'":
65 quote = char
66 elif char == "{":
67 depth += 1
68 elif char == "}":
69 depth -= 1
70 index += 1
71 return depth > 0