Commit 2b828ca8 authored by Anders Hartvoll Ruud's avatar Anders Hartvoll Ruud Committed by Commit Bot

Support multiple shorthand expansions in the code generator

A longhand that's part of some shorthand expansion needs manual
attention in the code template if that longhand is behind a flag.
This CL fixes that by generating shorthand expansions for all possible
runtime flag configurations (for relevant flags), and selecting the
correct one dynamically.

We assume that the flag-protected longhand will appear/disappear
"in-place" in the list of longhands. In other words, if some runtime
flag were to reorder longhands, manual code will still be needed in the
template.

Since this CL means we'll generate 2^N expansions for N flags, there's
currently a limit of 4 flags per shorthand (which should be generous
enough).

Bug: 1096015
Fixed: 1094236

Change-Id: I99d321b971ea2b746688829f35a868db7d0f5f3b
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2250242
Commit-Queue: Anders Hartvoll Ruud <andruud@chromium.org>
Reviewed-by: default avatarFredrik Söderquist <fs@opera.com>
Cr-Commit-Position: refs/heads/master@{#780062}
parent a4311850
......@@ -353,6 +353,10 @@ class CSSProperties(object):
x for x in self._aliases if not x['longhands']
]
@property
def properties_by_name(self):
return self._properties_by_name
@property
def properties_by_id(self):
return self._properties_by_id
......
......@@ -34,6 +34,67 @@ from name_utilities import enum_key_for_css_property, id_for_css_property
import template_expander
def collect_runtime_flags(properties):
"""Returns a list of unique runtime flags used by the properties"""
flags = {p['runtime_flag'] for p in properties if p['runtime_flag']}
return sorted(flags)
class Expansion(object):
"""A specific (longhand) expansion of a shorthand.
A shorthand may have multiple expansions, because some of the longhands
might be behind runtime flags.
The enabled_mask represents which flags are enabled/disabled for this
specific expansion. For example, if flags contains three elements,
and enabled_mask is 0b100, then flags[0] is disabled, flags[1] is disabled,
and flags[2] is enabled. This information is used to produce the correct
list of longhands corresponding to the runtime flags that are enabled/
disabled.
"""
def __init__(self, longhands, flags, enabled_mask):
super(Expansion, self).__init__()
self._longhands = longhands
self._flags = flags
self._enabled_mask = enabled_mask
def is_enabled(self, flag):
return (1 << self._flags.index(flag)) & self._enabled_mask
@property
def is_empty(self):
return len(self.enabled_longhands) == 0
@property
def enabled_longhands(self):
include = lambda longhand: not longhand[
'runtime_flag'] or self.is_enabled(longhand['runtime_flag'])
return filter(include, self._longhands)
@property
def index(self):
return self._enabled_mask
@property
def flags(self):
return [
dict(name=flag, enabled=self.is_enabled(flag))
for flag in self._flags
]
def create_expansions(longhands):
flags = collect_runtime_flags(longhands)
expansions = map(lambda mask: Expansion(longhands, flags, mask),
range(1 << len(flags)))
assert len(expansions) > 0
# We generate 2^N expansions for N flags, so enforce some limit.
assert len(flags) <= 4, 'Too many runtime flags for a single shorthand'
return expansions
class StylePropertyShorthandWriter(json5_generator.Writer):
class_name = 'StylePropertyShorthand'
_FILE_BASENAME = 'style_property_shorthand'
......@@ -57,6 +118,11 @@ class StylePropertyShorthandWriter(json5_generator.Writer):
property_['longhands'])
property_['longhand_property_ids'] = map(id_for_css_property,
property_['longhands'])
longhands = map(
lambda name: json5_properties.properties_by_name[name],
property_['longhands'])
property_['expansions'] = create_expansions(longhands)
for longhand_enum_key in property_['longhand_enum_keys']:
self._longhand_dictionary[longhand_enum_key].append(property_)
......
......@@ -27,63 +27,71 @@
#include "base/stl_util.h" // for base::size()
#include "third_party/blink/renderer/platform/runtime_enabled_features.h"
namespace blink {
{% for property in properties %}
const StylePropertyShorthand& {{property.name.to_lower_camel_case()}}Shorthand() {
{% macro define_shorthand(property, expansion) -%}
static const CSSProperty* longhands[] = {
{% for longhand_id in property.longhand_property_ids %}
&Get{{longhand_id}}(),
{% for longhand in expansion.enabled_longhands %}
&Get{{longhand.property_id}}(),
{% endfor %}
};
static const StylePropertyShorthand shorthand(
CSSPropertyID::{{property.enum_key}}, longhands, base::size(longhands));
return shorthand;
}
{% endfor %}
{%- endmacro %}
// TODO(ericwilligers): Retire this when offset-position and offset-anchor ship
const StylePropertyShorthand& offsetShorthandWithoutPositionAnchor() {
static const CSSProperty* longhands[] = {
&GetCSSPropertyOffsetPath(),
&GetCSSPropertyOffsetDistance(),
&GetCSSPropertyOffsetRotate(),
};
static const StylePropertyShorthand shorthand(
CSSPropertyID::kOffset, longhands, base::size(longhands));
return shorthand;
namespace blink {
{% for property in properties %}
{% set function_prefix = property.name.to_lower_camel_case() %}
{% for expansion in property.expansions[1:] %}
static const StylePropertyShorthand* {{function_prefix}}Shorthand{{expansion.index}}() {
{% for flag in expansion.flags %}
if ({{flag.enabled and '!' or ''}}RuntimeEnabledFeatures::{{flag.name}}Enabled())
return nullptr;
{% endfor %}
{{define_shorthand(property, expansion)}}
return &shorthand;
}
{% endfor %}
// TODO(https://crbug.com/1094236): Remove this override once text-decoration-thickness ships.
const StylePropertyShorthand& textDecorationShorthandWithoutThickness() {
static const CSSProperty* longhands[] = {
&GetCSSPropertyTextDecorationLine(),
&GetCSSPropertyTextDecorationStyle(),
&GetCSSPropertyTextDecorationColor(),
};
static const StylePropertyShorthand shorthand(
CSSPropertyID::kTextDecoration, longhands, base::size(longhands));
const StylePropertyShorthand& {{function_prefix}}Shorthand() {
{% if property.expansions|length > 1 %}
{% for expansion in property.expansions[1:] %}
if (const auto* s = {{function_prefix}}Shorthand{{expansion.index}}())
return *s;
{% endfor %}
{% endif %}
{% if property.expansions[0].flags %}
{% for flag in property.expansions[0].flags %}
DCHECK({{not flag.enabled and '!' or ''}}RuntimeEnabledFeatures::{{flag.name}}Enabled());
{% endfor %}
{% endif %}
{# Note: only expansions[0] can be empty #}
{% if property.expansions[0].is_empty %}
static StylePropertyShorthand empty_shorthand;
return empty_shorthand;
{% else %}
{{define_shorthand(property, property.expansions[0])}}
return shorthand;
{% endif %}
}
{% endfor %}
// Returns an empty list if the property is not a shorthand
const StylePropertyShorthand& shorthandForProperty(CSSPropertyID propertyID) {
// FIXME: We shouldn't switch between shorthand/not shorthand based on a runtime flag
static StylePropertyShorthand emptyShorthand;
static StylePropertyShorthand empty_shorthand;
if (propertyID == CSSPropertyID::kOffset &&
!RuntimeEnabledFeatures::CSSOffsetPositionAnchorEnabled())
return offsetShorthandWithoutPositionAnchor();
if (propertyID == CSSPropertyID::kTextDecoration &&
!RuntimeEnabledFeatures::UnderlineOffsetThicknessEnabled())
return textDecorationShorthandWithoutThickness();
switch (propertyID) {
{% for property in properties %}
{% set function_prefix = property.name.to_lower_camel_case() %}
case CSSPropertyID::{{property.enum_key}}:
return {{property.name.to_lower_camel_case()}}Shorthand();
return {{function_prefix}}Shorthand();
{% endfor %}
default: {
return emptyShorthand;
return empty_shorthand;
}
}
}
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment