Skip to content

Commit

Permalink
Support var in some shorthand functions
Browse files Browse the repository at this point in the history
Related to #1219.
  • Loading branch information
liZe committed Nov 26, 2023
1 parent 7f6b128 commit aceeeca
Show file tree
Hide file tree
Showing 5 changed files with 89 additions and 46 deletions.
22 changes: 20 additions & 2 deletions weasyprint/css/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,11 +23,14 @@
from ..logger import LOGGER, PROGRESS_LOGGER
from ..urls import URLFetchingError, get_url_attribute, url_join
from . import counters, media_queries
from .computed_values import COMPUTER_FUNCTIONS, ZERO_PIXELS, compute_var
from .computed_values import (
COMPUTER_FUNCTIONS, ZERO_PIXELS, compute_var, resolve_var)
from .properties import INHERITED, INITIAL_NOT_COMPUTED, INITIAL_VALUES
from .utils import get_url, remove_whitespace
from .utils import check_var_function, get_url, remove_whitespace
from .validation import preprocess_declarations
from .validation.descriptors import preprocess_descriptors
from .validation.expanders import PendingExpander
from .validation.properties import validate_non_shorthand

# Reject anything not in here:
PSEUDO_ELEMENTS = (
Expand Down Expand Up @@ -728,6 +731,21 @@ def __missing__(self, key):
# Value already computed and saved: return.
return self[key]

if isinstance(value, PendingExpander):
solved_tokens = []
for token in value.tokens:
variable = check_var_function(token)
if variable:
variable_name, default = variable[1]
tokens = resolve_var(
self, variable_name, default, parent_style)
solved_tokens.extend(tokens)
else:
solved_tokens.append(token)
original_key = key.replace('_', '-')
solved = value.solve(solved_tokens, original_key)
value = validate_non_shorthand(None, original_key, solved)[0][1]

if not computed and key in COMPUTER_FUNCTIONS:
# Value not computed yet: compute.
value = COMPUTER_FUNCTIONS[key](self, key, value)
Expand Down
4 changes: 2 additions & 2 deletions weasyprint/css/computed_values.py
Original file line number Diff line number Diff line change
Expand Up @@ -150,7 +150,7 @@ def _computing_order():
COMPUTER_FUNCTIONS = {}


def _resolve_var(computed, variable_name, default, parent_style):
def resolve_var(computed, variable_name, default, parent_style):
known_variable_names = [variable_name]

computed_value = computed[variable_name]
Expand Down Expand Up @@ -246,7 +246,7 @@ def compute_var(name, computed_style, parent_style):
validator = PROPERTIES[validation_name]
for i, variable in variables.items():
variable_name, default = variable
value = _resolve_var(
value = resolve_var(
computed_style, variable_name, default, parent_style)

if value is not None:
Expand Down
4 changes: 2 additions & 2 deletions weasyprint/css/validation/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -111,11 +111,11 @@ def validation_error(level, reason):
validation_error('debug', 'prefixed selectors are ignored')
continue

expander_ = EXPANDERS.get(name, validate_non_shorthand)
expander = EXPANDERS.get(name, validate_non_shorthand)
tokens = remove_whitespace(declaration.value)
try:
# Use list() to consume generators now and catch any error.
result = list(expander_(base_url, name, tokens))
result = list(expander(base_url, name, tokens))
except InvalidValues as exc:
validation_error(
'warning',
Expand Down
103 changes: 64 additions & 39 deletions weasyprint/css/validation/expanders.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,8 @@

from ..properties import INITIAL_VALUES
from ..utils import (
InvalidValues, get_keyword, get_single_keyword, split_on_comma)
InvalidValues, check_var_function, get_keyword, get_single_keyword,
split_on_comma)
from .descriptors import expand_font_variant
from .properties import (
background_attachment, background_image, background_position,
Expand All @@ -21,6 +22,20 @@
EXPANDERS = {}


class PendingExpander:
def __init__(self, tokens, expander):
self.tokens = tokens
self.expander = expander

def solve(self, tokens, wanted_key):
for key, value in self.expander(tokens=tokens):
if key.startswith('-'):
key = f'{self.expander.keywords["name"]}{key}'
if key == wanted_key:
return value
raise KeyError


def expander(property_name):
"""Decorator adding a function to the ``EXPANDERS``."""
def expander_decorator(function):
Expand All @@ -31,39 +46,6 @@ def expander_decorator(function):
return expander_decorator


@expander('border-color')
@expander('border-style')
@expander('border-width')
@expander('margin')
@expander('padding')
@expander('bleed')
def expand_four_sides(base_url, name, tokens):
"""Expand properties setting a token for the four sides of a box."""
# Make sure we have 4 tokens
if len(tokens) == 1:
tokens *= 4
elif len(tokens) == 2:
tokens *= 2 # (bottom, left) defaults to (top, right)
elif len(tokens) == 3:
tokens += (tokens[1],) # left defaults to right
elif len(tokens) != 4:
raise InvalidValues(
f'Expected 1 to 4 token components got {len(tokens)}')
for suffix, token in zip(('-top', '-right', '-bottom', '-left'), tokens):
i = name.rfind('-')
if i == -1:
new_name = name + suffix
else:
# eg. border-color becomes border-*-color, not border-color-*
new_name = name[:i] + suffix + name[i:]

# validate_non_shorthand returns ((name, value),), we want
# to yield (name, value)
result, = validate_non_shorthand(
base_url, new_name, [token], required=True)
yield result


def generic_expander(*expanded_names, **kwargs):
"""Decorator helping expanders to handle ``inherit`` and ``initial``.
Expand All @@ -80,17 +62,27 @@ def generic_expander_decorator(wrapped):
@functools.wraps(wrapped)
def generic_expander_wrapper(base_url, name, tokens):
"""Wrap the expander."""
expander = functools.partial(wrapped, name=name)
if wants_base_url:
expander = functools.partial(expander, base_url=base_url)

skip_validation = False
keyword = get_single_keyword(tokens)
if keyword in ('inherit', 'initial'):
results = dict.fromkeys(expanded_names, keyword)
skip_validation = True
else:
skip_validation = False
for token in tokens:
if check_var_function(token):
# Found CSS variable, keep pending-substitution values.
pending = PendingExpander(tokens, expander)
results = dict.fromkeys(expanded_names, pending)
skip_validation = True
break

if not skip_validation:
results = {}
if wants_base_url:
result = wrapped(name, tokens, base_url)
else:
result = wrapped(name, tokens)
result = expander(tokens=tokens)
for new_name, new_token in result:
assert new_name in expanded_names, new_name
if new_name in results:
Expand Down Expand Up @@ -120,6 +112,39 @@ def generic_expander_wrapper(base_url, name, tokens):
return generic_expander_decorator


@expander('border-color')
@expander('border-style')
@expander('border-width')
@expander('margin')
@expander('padding')
@expander('bleed')
def expand_four_sides(base_url, name, tokens):
"""Expand properties setting a token for the four sides of a box."""
# Make sure we have 4 tokens
if len(tokens) == 1:
tokens *= 4
elif len(tokens) == 2:
tokens *= 2 # (bottom, left) defaults to (top, right)
elif len(tokens) == 3:
tokens += (tokens[1],) # left defaults to right
elif len(tokens) != 4:
raise InvalidValues(
f'Expected 1 to 4 token components got {len(tokens)}')
for suffix, token in zip(('-top', '-right', '-bottom', '-left'), tokens):
i = name.rfind('-')
if i == -1:
new_name = name + suffix
else:
# eg. border-color becomes border-*-color, not border-color-*
new_name = name[:i] + suffix + name[i:]

# validate_non_shorthand returns ((name, value),), we want
# to yield (name, value)
result, = validate_non_shorthand(
base_url, new_name, [token], required=True)
yield result


@expander('border-radius')
@generic_expander(
'border-top-left-radius', 'border-top-right-radius',
Expand Down
2 changes: 1 addition & 1 deletion weasyprint/css/validation/properties.py
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ def decorator(function):


def validate_non_shorthand(base_url, name, tokens, required=False):
"""Default validator for non-shorthand properties."""
"""Validator for non-shorthand properties."""
if name.startswith('--'):
# TODO: validate content
return ((name, tokens),)
Expand Down

0 comments on commit aceeeca

Please sign in to comment.