Skip to content

Commit

Permalink
Allow var functions in shorthand properties
Browse files Browse the repository at this point in the history
Related to #1219.
  • Loading branch information
liZe committed Nov 28, 2023
1 parent 374140a commit 29ca559
Show file tree
Hide file tree
Showing 6 changed files with 280 additions and 75 deletions.
153 changes: 151 additions & 2 deletions tests/test_variables.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
import pytest
from weasyprint.css.properties import KNOWN_PROPERTIES

from .testing_utils import assert_no_logs, render_pages
from .testing_utils import assert_no_logs, capture_logs, render_pages

SIDES = ('top', 'right', 'bottom', 'left')

Expand Down Expand Up @@ -106,7 +106,7 @@ def test_variable_chain_root_missing():


@assert_no_logs
def test_variable_partial_1():
def test_variable_shorthand_margin():
page, = render_pages('''
<style>
html { --var: 10px }
Expand All @@ -123,6 +123,155 @@ def test_variable_partial_1():
assert div.margin_left == 10


@assert_no_logs
def test_variable_shorthand_margin_multiple():
page, = render_pages('''
<style>
html { --var1: 10px; --var2: 20px }
div { margin: var(--var2) 0 0 var(--var1) }
</style>
<div></div>
''')
html, = page.children
body, = html.children
div, = body.children
assert div.margin_top == 20
assert div.margin_right == 0
assert div.margin_bottom == 0
assert div.margin_left == 10


@assert_no_logs
def test_variable_shorthand_margin_invalid():
with capture_logs() as logs:
page, = render_pages('''
<style>
html { --var: blue }
div { margin: 0 0 0 var(--var) }
</style>
<div></div>
''')
log, = logs
assert 'invalid value' in log
html, = page.children
body, = html.children
div, = body.children
assert div.margin_top == 0
assert div.margin_right == 0
assert div.margin_bottom == 0
assert div.margin_left == 0


@assert_no_logs
def test_variable_shorthand_border():
page, = render_pages('''
<style>
html { --var: 1px solid blue }
div { border: var(--var) }
</style>
<div></div>
''')
html, = page.children
body, = html.children
div, = body.children
assert div.border_top_width == 1
assert div.border_right_width == 1
assert div.border_bottom_width == 1
assert div.border_left_width == 1


@assert_no_logs
def test_variable_shorthand_border_side():
page, = render_pages('''
<style>
html { --var: 1px solid blue }
div { border-top: var(--var) }
</style>
<div></div>
''')
html, = page.children
body, = html.children
div, = body.children
assert div.border_top_width == 1
assert div.border_right_width == 0
assert div.border_bottom_width == 0
assert div.border_left_width == 0


@assert_no_logs
def test_variable_shorthand_border_mixed():
page, = render_pages('''
<style>
html { --var: 1px solid }
div { border: blue var(--var) }
</style>
<div></div>
''')
html, = page.children
body, = html.children
div, = body.children
assert div.border_top_width == 1
assert div.border_right_width == 1
assert div.border_bottom_width == 1
assert div.border_left_width == 1


def test_variable_shorthand_border_mixed_invalid():
with capture_logs() as logs:
page, = render_pages('''
<style>
html { --var: 1px solid blue }
div { border: blue var(--var) }
</style>
<div></div>
''')
# TODO: we should only get one warning here
assert 'multiple color values' in logs[0]
html, = page.children
body, = html.children
div, = body.children
assert div.border_top_width == 0
assert div.border_right_width == 0
assert div.border_bottom_width == 0
assert div.border_left_width == 0


@assert_no_logs
@pytest.mark.parametrize('var, background', (
('blue', 'var(--v)'),
('padding-box url(pattern.png)', 'var(--v)'),
('padding-box url(pattern.png)', 'white var(--v) center'),
('100%', 'url(pattern.png) var(--v) var(--v) / var(--v) var(--v)'),
('left / 100%', 'url(pattern.png) top var(--v) 100%'),
))
def test_variable_shorthand_background(var, background):
page, = render_pages('''
<style>
html { --v: %s }
div { background: %s }
</style>
<div></div>
''' % (var, background))


@pytest.mark.parametrize('var, background', (
('invalid', 'var(--v)'),
('blue', 'var(--v) var(--v)'),
('100%', 'url(pattern.png) var(--v) var(--v) var(--v)'),
))
def test_variable_shorthand_background_invalid(var, background):
with capture_logs() as logs:
page, = render_pages('''
<style>
html { --v: %s }
div { background: %s }
</style>
<div></div>
''' % (var, background))
log, = logs
assert 'invalid value' in log


@assert_no_logs
def test_variable_initial():
page, = render_pages('''
Expand Down
21 changes: 17 additions & 4 deletions weasyprint/css/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,11 +26,11 @@
from .computed_values import (
COMPUTER_FUNCTIONS, ZERO_PIXELS, compute_var, resolve_var)
from .properties import INHERITED, INITIAL_NOT_COMPUTED, INITIAL_VALUES
from .utils import check_var_function, get_url, remove_whitespace
from .utils import (
InvalidValues, 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 @@ -732,6 +732,8 @@ def __missing__(self, key):
return self[key]

if isinstance(value, PendingExpander):
# Expander with pending values, validate them.
computed = False
solved_tokens = []
for token in value.tokens:
variable = check_var_function(token)
Expand All @@ -743,8 +745,19 @@ def __missing__(self, key):
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]
try:
value = value.solve(solved_tokens, original_key)
except InvalidValues:
if key in INHERITED:
# Values in parent_style are already computed.
self[key] = value = parent_style[key]
return value
else:
value = INITIAL_VALUES[key]
if key not in INITIAL_NOT_COMPUTED:
# The value is the same as when computed.
self[key] = value
return value

if not computed and key in COMPUTER_FUNCTIONS:
# Value not computed yet: compute.
Expand Down
2 changes: 1 addition & 1 deletion weasyprint/css/validation/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -115,7 +115,7 @@ def validation_error(level, reason):
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(tokens, name, base_url))
except InvalidValues as exc:
validation_error(
'warning',
Expand Down
2 changes: 1 addition & 1 deletion weasyprint/css/validation/descriptors.py
Original file line number Diff line number Diff line change
Expand Up @@ -198,7 +198,7 @@ def font_variant(tokens):
for name, sub_tokens in expand_font_variant(tokens):
try:
values.append(properties.validate_non_shorthand(
None, f'font-variant{name}', sub_tokens, required=True))
sub_tokens, f'font-variant{name}', None, required=True))
except InvalidValues:
return None
return values
Expand Down
Loading

0 comments on commit 29ca559

Please sign in to comment.