LibWeb: Make computed properties immutable

Move CSS animation values into a mutable overlay on computed properties
and make base computed style data immutable after construction. Base
style mutation now goes through a builder that is consumed on publish,
so installed styles no longer expose mutation APIs.

Build new base style data for inherited style updates instead of cloning
and mutating installed computed properties. Element-specific computed
style adjustments now run before publication, while animation and
transition updates continue to mutate only the animated overlay.
This commit is contained in:
Andreas Kling 2026-06-19 16:49:20 +02:00 committed by Andreas Kling
parent d7c08964cb
commit 2bf1f41805
64 changed files with 834 additions and 330 deletions

View file

@ -31,6 +31,22 @@ namespace Web::Animations {
GC_DEFINE_ALLOCATOR(AnimationEffect);
AnimationUpdateContext::ElementData::ElementData() = default;
AnimationUpdateContext::ElementData::ElementData(RefPtr<CSS::AnimatedProperties const> animated_properties_before_update, RefPtr<CSS::ComputedProperties> target_style)
: animated_properties_before_update(move(animated_properties_before_update))
, target_style(move(target_style))
{
}
AnimationUpdateContext::ElementData::ElementData(ElementData&&) = default;
AnimationUpdateContext::ElementData& AnimationUpdateContext::ElementData::operator=(ElementData&&) = default;
AnimationUpdateContext::ElementData::~ElementData() = default;
AnimationUpdateContext::AnimationUpdateContext() = default;
Bindings::FillMode css_fill_mode_to_bindings_fill_mode(CSS::AnimationFillMode mode)
{
switch (mode) {
@ -787,20 +803,34 @@ void AnimationEffect::visit_edges(JS::Cell::Visitor& visitor)
visitor.visit(m_associated_animation);
}
static CSS::RequiredInvalidationAfterStyleChange compute_required_invalidation_for_animated_properties(HashMap<CSS::PropertyID, NonnullRefPtr<CSS::StyleValue const>> const& old_properties, HashMap<CSS::PropertyID, NonnullRefPtr<CSS::StyleValue const>> const& new_properties)
static CSS::StyleValue const* animated_property_value(CSS::AnimatedProperties const* properties, CSS::PropertyID property_id)
{
if (!properties)
return nullptr;
auto value = properties->values().get(property_id);
if (!value.has_value())
return nullptr;
return value.value();
}
static CSS::RequiredInvalidationAfterStyleChange compute_required_invalidation_for_animated_properties(CSS::AnimatedProperties const* old_properties, CSS::AnimatedProperties const* new_properties)
{
CSS::RequiredInvalidationAfterStyleChange invalidation;
auto old_and_new_properties = MUST(Bitmap::create(CSS::number_of_longhand_properties, 0));
for (auto const& [property_id, _] : old_properties)
old_and_new_properties.set(to_underlying(property_id) - to_underlying(CSS::first_longhand_property_id), 1);
for (auto const& [property_id, _] : new_properties)
old_and_new_properties.set(to_underlying(property_id) - to_underlying(CSS::first_longhand_property_id), 1);
if (old_properties) {
for (auto const& [property_id, _] : old_properties->values())
old_and_new_properties.set(to_underlying(property_id) - to_underlying(CSS::first_longhand_property_id), 1);
}
if (new_properties) {
for (auto const& [property_id, _] : new_properties->values())
old_and_new_properties.set(to_underlying(property_id) - to_underlying(CSS::first_longhand_property_id), 1);
}
for (auto i = to_underlying(CSS::first_longhand_property_id); i <= to_underlying(CSS::last_longhand_property_id); ++i) {
if (!old_and_new_properties.get(i - to_underlying(CSS::first_longhand_property_id)))
continue;
auto property_id = static_cast<CSS::PropertyID>(i);
auto const* old_value = old_properties.get(property_id).value_or({});
auto const* new_value = new_properties.get(property_id).value_or({});
auto const* old_value = animated_property_value(old_properties, property_id);
auto const* new_value = animated_property_value(new_properties, property_id);
if (!old_value && !new_value)
continue;
auto property_invalidation = compute_property_invalidation(property_id, old_value, new_value);
@ -819,7 +849,8 @@ AnimationUpdateContext::~AnimationUpdateContext()
continue;
auto& element = it.key;
GC::Ref<DOM::Element> target = element.element();
auto invalidation = compute_required_invalidation_for_animated_properties(it.value.animated_properties_before_update, style->animated_property_values());
auto animated_properties_after_update = style->animated_properties_snapshot();
auto invalidation = compute_required_invalidation_for_animated_properties(it.value.animated_properties_before_update.ptr(), animated_properties_after_update.ptr());
if (invalidation.is_none())
continue;

View file

@ -15,6 +15,12 @@
#include <LibWeb/Bindings/PlatformObject.h>
#include <LibWeb/CSS/EasingFunction.h>
namespace Web::CSS {
class AnimatedProperties;
}
namespace Web::Animations {
enum class AnimationDirection {
@ -29,11 +35,17 @@ Bindings::OptionalEffectTiming to_optional_effect_timing(Bindings::EffectTiming
// This object lives for the duration of an animation update, and is used to store per-element data about animated CSS properties.
struct AnimationUpdateContext {
struct ElementData {
using PropertyMap = HashMap<CSS::PropertyID, NonnullRefPtr<CSS::StyleValue const>>;
PropertyMap animated_properties_before_update;
ElementData();
ElementData(RefPtr<CSS::AnimatedProperties const>, RefPtr<CSS::ComputedProperties>);
ElementData(ElementData&&);
ElementData& operator=(ElementData&&);
~ElementData();
RefPtr<CSS::AnimatedProperties const> animated_properties_before_update;
RefPtr<CSS::ComputedProperties> target_style;
};
AnimationUpdateContext();
~AnimationUpdateContext();
// NOTE: This is lazily populated by KeyframeEffects as their respective animations are applied to an element.

View file

@ -970,17 +970,18 @@ void KeyframeEffect::update_computed_properties(AnimationUpdateContext& context)
return;
}
auto computed_properties = target->computed_properties(pseudo_element_type());
if (!computed_properties)
return;
DOM::AbstractElement abstract_element { *target, pseudo_element_type() };
context.elements.ensure(abstract_element, [computed_properties] {
auto old_animated_properties = computed_properties->animated_property_values();
computed_properties->reset_non_inherited_animated_properties({});
target->update_animated_properties({}, pseudo_element_type(), *this, context);
}
void KeyframeEffect::update_computed_properties_for_style(AnimationUpdateContext& context, DOM::AbstractElement abstract_element, CSS::ComputedProperties& computed_properties)
{
context.elements.ensure(abstract_element, [&computed_properties] {
auto old_animated_properties = computed_properties.animated_properties_snapshot();
computed_properties.reset_non_inherited_animated_properties({});
return AnimationUpdateContext::ElementData { move(old_animated_properties), computed_properties };
});
target->document().style_computer().collect_animation_into(abstract_element, *this, *computed_properties);
abstract_element.element().document().style_computer().collect_animation_into(abstract_element, *this, computed_properties);
}
Bindings::CompositeOperation css_animation_composition_to_bindings_composite_operation(CSS::AnimationComposition composition)

View file

@ -107,6 +107,7 @@ public:
virtual bool is_keyframe_effect() const override { return true; }
virtual void update_computed_properties(AnimationUpdateContext&) override;
void update_computed_properties_for_style(AnimationUpdateContext&, DOM::AbstractElement, CSS::ComputedProperties&);
private:
KeyframeEffect(JS::Realm&);

View file

@ -85,7 +85,7 @@ static bool should_update_timeline(GC::Ptr<Animations::AnimationTimeline> old_ti
return true;
}
void CSSAnimation::apply_css_properties(ComputedProperties::AnimationProperties const& animation_properties)
void CSSAnimation::apply_css_properties(AnimationProperties const& animation_properties)
{
// FIXME: Don't apply overridden properties as defined here: https://drafts.csswg.org/css-animations-2/#animations

View file

@ -7,7 +7,7 @@
#pragma once
#include <LibWeb/Animations/Animation.h>
#include <LibWeb/CSS/ComputedProperties.h>
#include <LibWeb/CSS/CSSAnimationProperties.h>
#include <LibWeb/CSS/EasingFunction.h>
#include <LibWeb/CSS/StyleValues/StyleValue.h>
@ -27,7 +27,7 @@ public:
virtual Animations::AnimationClass animation_class() const override;
virtual int class_specific_composite_order(GC::Ref<Animations::Animation> other) const override;
void apply_css_properties(ComputedProperties::AnimationProperties const&);
void apply_css_properties(AnimationProperties const&);
EasingFunction const& default_easing() const { return m_default_easing; }

View file

@ -0,0 +1,32 @@
/*
* Copyright (c) 2026-present, the Ladybird developers.
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#pragma once
#include <AK/FlyString.h>
#include <AK/String.h>
#include <AK/Variant.h>
#include <LibGC/Ptr.h>
#include <LibWeb/CSS/ComputedValues.h>
#include <LibWeb/CSS/EasingFunction.h>
#include <LibWeb/Forward.h>
namespace Web::CSS {
struct AnimationProperties {
Variant<double, String> duration;
EasingFunction timing_function;
double iteration_count;
AnimationDirection direction;
AnimationPlayState play_state;
double delay;
AnimationFillMode fill_mode;
AnimationComposition composition;
FlyString name;
GC::Ptr<Animations::AnimationTimeline> timeline;
};
}

View file

@ -13,6 +13,7 @@
#include <LibWeb/CSS/CSSStyleProperties.h>
#include <LibWeb/CSS/CSSStyleSheet.h>
#include <LibWeb/CSS/ComputedProperties.h>
#include <LibWeb/CSS/CustomPropertyData.h>
#include <LibWeb/CSS/Parser/Parser.h>
#include <LibWeb/CSS/PropertyNameAndID.h>
#include <LibWeb/CSS/StyleComputer.h>

View file

@ -5,6 +5,7 @@
* SPDX-License-Identifier: BSD-2-Clause
*/
#include <AK/NeverDestroyed.h>
#include <AK/NonnullRawPtr.h>
#include <AK/TypeCasts.h>
#include <LibCore/DirIterator.h>
@ -58,112 +59,247 @@ namespace Web::CSS {
static_assert(to_underlying(PseudoElement::KnownPseudoElementCount) <= sizeof(u64) * 8);
ComputedProperties::ComputedProperties() = default;
static size_t property_bitmap_index(PropertyID property_id)
{
VERIFY(property_id >= first_longhand_property_id && property_id <= last_longhand_property_id);
return to_underlying(property_id) - to_underlying(first_longhand_property_id);
}
ComputedProperties::Builder::Builder()
: m_data(adopt_ref(*new Data))
, m_style(adopt_ref(*new ComputedProperties(m_data, false, false)))
{
}
ComputedProperties::Builder::Builder(ComputedProperties const& style)
: Builder()
{
m_data->property_values = style.data().property_values;
m_data->property_important = style.data().property_important;
m_data->property_inherited = style.data().property_inherited;
m_data->display_before_box_type_transformation = style.data().display_before_box_type_transformation;
m_data->pseudo_element_styles = style.data().pseudo_element_styles;
m_data->line_height = style.data().line_height;
m_data->inheritance_dependent_specified_values = style.data().inheritance_dependent_specified_values;
m_data->raw_cascaded_font_size = style.data().raw_cascaded_font_size;
m_depends_on_viewport_metrics = style.depends_on_viewport_metrics();
m_font_metrics_depend_on_viewport_metrics = style.font_metrics_depend_on_viewport_metrics();
m_style->m_depends_on_viewport_metrics = m_depends_on_viewport_metrics;
m_style->m_font_metrics_depend_on_viewport_metrics = m_font_metrics_depend_on_viewport_metrics;
if (style.m_animated_properties)
m_style->m_animated_properties = adopt_ref(*new AnimatedProperties(*style.m_animated_properties));
}
NonnullRefPtr<ComputedProperties> ComputedProperties::Builder::build() &&
{
m_style->m_depends_on_viewport_metrics = m_depends_on_viewport_metrics;
m_style->m_font_metrics_depend_on_viewport_metrics = m_font_metrics_depend_on_viewport_metrics;
return move(m_style);
}
ComputedProperties::Builder ComputedProperties::create_builder()
{
return Builder {};
}
ComputedProperties::Builder ComputedProperties::create_builder_with_base_values_from(ComputedProperties const& style)
{
return Builder { style };
}
NonnullRefPtr<ComputedProperties> ComputedProperties::create(Builder&& builder)
{
return move(builder).build();
}
AnimatedProperties::AnimatedProperties(AnimatedProperties const& other)
: m_has_property(other.m_has_property)
, m_property_inherited(other.m_property_inherited)
, m_property_result_of_transition(other.m_property_result_of_transition)
, m_values(other.m_values)
{
}
ComputedProperties::ComputedProperties(NonnullRefPtr<Data const> data, bool depends_on_viewport_metrics, bool font_metrics_depend_on_viewport_metrics)
: m_data(move(data))
, m_depends_on_viewport_metrics(depends_on_viewport_metrics)
, m_font_metrics_depend_on_viewport_metrics(font_metrics_depend_on_viewport_metrics)
{
}
ComputedProperties::~ComputedProperties() = default;
NonnullRefPtr<ComputedProperties> ComputedProperties::create()
AnimatedProperties const& ComputedProperties::animated_properties() const
{
return adopt_ref(*new ComputedProperties);
static NeverDestroyed<AnimatedProperties> empty_animated_properties;
if (!m_animated_properties)
return *empty_animated_properties;
return *m_animated_properties;
}
AnimatedProperties& ComputedProperties::mutable_animated_properties()
{
if (!m_animated_properties)
m_animated_properties = adopt_ref(*new AnimatedProperties);
if (m_animated_properties->ref_count() > 1)
m_animated_properties = adopt_ref(*new AnimatedProperties(*m_animated_properties));
return *m_animated_properties;
}
bool AnimatedProperties::has_property(PropertyID property_id) const
{
return m_has_property.get(property_bitmap_index(property_id));
}
bool AnimatedProperties::is_property_inherited(PropertyID property_id) const
{
return m_property_inherited.get(property_bitmap_index(property_id));
}
bool AnimatedProperties::is_property_result_of_transition(PropertyID property_id) const
{
return m_property_result_of_transition.get(property_bitmap_index(property_id));
}
StyleValue const& AnimatedProperties::property(PropertyID property_id) const
{
VERIFY(property_id >= first_longhand_property_id && property_id <= last_longhand_property_id);
VERIFY(has_property(property_id));
auto animated_value = m_values.get(property_id);
VERIFY(animated_value.has_value());
return *animated_value.value();
}
void AnimatedProperties::set_property_inherited(PropertyID property_id, ComputedProperties::Inherited inherited)
{
m_property_inherited.set(property_bitmap_index(property_id), inherited == ComputedProperties::Inherited::Yes);
}
void AnimatedProperties::set_property_result_of_transition(PropertyID property_id, AnimatedPropertyResultOfTransition animated_value_result_of_transition)
{
m_property_result_of_transition.set(property_bitmap_index(property_id), animated_value_result_of_transition == AnimatedPropertyResultOfTransition::Yes);
}
void AnimatedProperties::set_property(PropertyID id, NonnullRefPtr<StyleValue const> value, AnimatedPropertyResultOfTransition animated_property_result_of_transition, ComputedProperties::Inherited inherited)
{
VERIFY(id >= first_longhand_property_id && id <= last_longhand_property_id);
m_values.set(id, move(value));
m_has_property.set(property_bitmap_index(id), true);
set_property_inherited(id, inherited);
set_property_result_of_transition(id, animated_property_result_of_transition);
}
void AnimatedProperties::remove_property(PropertyID id)
{
VERIFY(id >= first_longhand_property_id && id <= last_longhand_property_id);
m_values.remove(id);
m_has_property.set(property_bitmap_index(id), false);
set_property_inherited(id, ComputedProperties::Inherited::No);
set_property_result_of_transition(id, AnimatedPropertyResultOfTransition::No);
}
void AnimatedProperties::reset_non_inherited_properties()
{
for (auto property_id : m_values.keys()) {
if (!is_property_inherited(property_id))
remove_property(property_id);
}
}
bool ComputedProperties::is_property_important(PropertyID property_id) const
{
VERIFY(property_id >= first_longhand_property_id && property_id <= last_longhand_property_id);
size_t n = to_underlying(property_id) - to_underlying(first_longhand_property_id);
return m_property_important[n / 8] & (1 << (n % 8));
return data().property_important.get(property_bitmap_index(property_id));
}
void ComputedProperties::set_property_important(PropertyID property_id, Important important)
void ComputedProperties::Builder::set_property_important(PropertyID property_id, Important important)
{
VERIFY(property_id >= first_longhand_property_id && property_id <= last_longhand_property_id);
size_t n = to_underlying(property_id) - to_underlying(first_longhand_property_id);
if (important == Important::Yes)
m_property_important[n / 8] |= (1 << (n % 8));
else
m_property_important[n / 8] &= ~(1 << (n % 8));
data().property_important.set(property_bitmap_index(property_id), important == Important::Yes);
}
bool ComputedProperties::is_property_inherited(PropertyID property_id) const
{
VERIFY(property_id >= first_longhand_property_id && property_id <= last_longhand_property_id);
return data().property_inherited.get(property_bitmap_index(property_id));
}
size_t n = to_underlying(property_id) - to_underlying(first_longhand_property_id);
return m_property_inherited[n / 8] & (1 << (n % 8));
HashMap<PropertyID, NonnullRefPtr<StyleValue const>> const& ComputedProperties::animated_property_values() const
{
return animated_properties().values();
}
RefPtr<AnimatedProperties const> ComputedProperties::animated_properties_snapshot() const
{
return m_animated_properties;
}
bool ComputedProperties::has_animated_property(PropertyID property_id) const
{
return animated_properties().has_property(property_id);
}
bool ComputedProperties::is_animated_property_inherited(PropertyID property_id) const
{
VERIFY(property_id >= first_longhand_property_id && property_id <= last_longhand_property_id);
size_t n = to_underlying(property_id) - to_underlying(first_longhand_property_id);
return m_animated_property_inherited[n / 8] & (1 << (n % 8));
return animated_properties().is_property_inherited(property_id);
}
bool ComputedProperties::is_animated_property_result_of_transition(PropertyID property_id) const
{
VERIFY(property_id >= first_longhand_property_id && property_id <= last_longhand_property_id);
size_t n = to_underlying(property_id) - to_underlying(first_longhand_property_id);
return m_animated_property_result_of_transition[n / 8] & (1 << (n % 8));
return animated_properties().is_property_result_of_transition(property_id);
}
bool ComputedProperties::has_pseudo_element_style(PseudoElement pseudo_element) const
{
VERIFY(to_underlying(pseudo_element) < to_underlying(PseudoElement::KnownPseudoElementCount));
return m_pseudo_element_styles & (1ull << to_underlying(pseudo_element));
return data().pseudo_element_styles & (1ull << to_underlying(pseudo_element));
}
void ComputedProperties::set_has_pseudo_element_styles(u64 pseudo_element_styles)
void ComputedProperties::Builder::set_has_pseudo_element_styles(u64 pseudo_element_styles)
{
constexpr auto known_pseudo_element_count = to_underlying(PseudoElement::KnownPseudoElementCount);
if constexpr (known_pseudo_element_count < sizeof(u64) * 8)
VERIFY((pseudo_element_styles >> known_pseudo_element_count) == 0);
m_pseudo_element_styles |= pseudo_element_styles;
data().pseudo_element_styles |= pseudo_element_styles;
}
void ComputedProperties::set_property_inherited(PropertyID property_id, Inherited inherited)
void ComputedProperties::Builder::set_property_inherited(PropertyID property_id, Inherited inherited)
{
VERIFY(property_id >= first_longhand_property_id && property_id <= last_longhand_property_id);
size_t n = to_underlying(property_id) - to_underlying(first_longhand_property_id);
if (inherited == Inherited::Yes)
m_property_inherited[n / 8] |= (1 << (n % 8));
else
m_property_inherited[n / 8] &= ~(1 << (n % 8));
data().property_inherited.set(property_bitmap_index(property_id), inherited == Inherited::Yes);
}
void ComputedProperties::set_animated_property_inherited(PropertyID property_id, Inherited inherited)
void ComputedProperties::Builder::set_depends_on_viewport_metrics()
{
VERIFY(property_id >= first_longhand_property_id && property_id <= last_longhand_property_id);
size_t n = to_underlying(property_id) - to_underlying(first_longhand_property_id);
if (inherited == Inherited::Yes)
m_animated_property_inherited[n / 8] |= (1 << (n % 8));
else
m_animated_property_inherited[n / 8] &= ~(1 << (n % 8));
m_depends_on_viewport_metrics = true;
m_style->m_depends_on_viewport_metrics = true;
}
void ComputedProperties::set_animated_property_result_of_transition(PropertyID property_id, AnimatedPropertyResultOfTransition animated_value_result_of_transition)
void ComputedProperties::Builder::set_font_metrics_depend_on_viewport_metrics()
{
VERIFY(property_id >= first_longhand_property_id && property_id <= last_longhand_property_id);
size_t n = to_underlying(property_id) - to_underlying(first_longhand_property_id);
if (animated_value_result_of_transition == AnimatedPropertyResultOfTransition::Yes)
m_animated_property_result_of_transition[n / 8] |= (1 << (n % 8));
else
m_animated_property_result_of_transition[n / 8] &= ~(1 << (n % 8));
m_font_metrics_depend_on_viewport_metrics = true;
m_style->m_font_metrics_depend_on_viewport_metrics = true;
}
void ComputedProperties::set_has_pseudo_element_style(PseudoElement pseudo_element)
void ComputedProperties::set_depends_on_viewport_metrics(Badge<StyleComputer>)
{
m_depends_on_viewport_metrics = true;
}
void ComputedProperties::set_font_metrics_depend_on_viewport_metrics(Badge<StyleComputer>)
{
m_font_metrics_depend_on_viewport_metrics = true;
}
void ComputedProperties::Builder::set_has_pseudo_element_style(PseudoElement pseudo_element)
{
VERIFY(to_underlying(pseudo_element) < to_underlying(PseudoElement::KnownPseudoElementCount));
m_pseudo_element_styles |= 1ull << to_underlying(pseudo_element);
data().pseudo_element_styles |= 1ull << to_underlying(pseudo_element);
}
void ComputedProperties::set_property(PropertyID id, NonnullRefPtr<StyleValue const> value, Inherited inherited, Important important)
void ComputedProperties::Builder::set_property(PropertyID id, NonnullRefPtr<StyleValue const> value, Inherited inherited, Important important)
{
VERIFY(id >= first_longhand_property_id && id <= last_longhand_property_id);
@ -177,56 +313,97 @@ static bool property_affects_computed_font_list(PropertyID id)
return first_is_one_of(id, PropertyID::FontFamily, PropertyID::FontSize, PropertyID::FontStyle, PropertyID::FontWeight, PropertyID::FontWidth, PropertyID::FontVariationSettings);
}
void ComputedProperties::set_property_without_modifying_flags(PropertyID id, NonnullRefPtr<StyleValue const> value)
void ComputedProperties::Builder::set_property_without_modifying_flags(PropertyID id, NonnullRefPtr<StyleValue const> value)
{
VERIFY(id >= first_longhand_property_id && id <= last_longhand_property_id);
m_property_values[to_underlying(id) - to_underlying(first_longhand_property_id)] = move(value);
data().property_values[to_underlying(id) - to_underlying(first_longhand_property_id)] = move(value);
if (property_affects_computed_font_list(id))
clear_computed_font_list_cache();
style().clear_computed_font_list_cache();
}
void ComputedProperties::revert_property(PropertyID id, ComputedProperties const& style_for_revert)
void ComputedProperties::Builder::revert_property(PropertyID id, ComputedProperties const& style_for_revert)
{
VERIFY(id >= first_longhand_property_id && id <= last_longhand_property_id);
m_property_values[to_underlying(id) - to_underlying(first_longhand_property_id)] = style_for_revert.m_property_values[to_underlying(id) - to_underlying(first_longhand_property_id)];
data().property_values[to_underlying(id) - to_underlying(first_longhand_property_id)] = style_for_revert.data().property_values[to_underlying(id) - to_underlying(first_longhand_property_id)];
set_property_important(id, style_for_revert.is_property_important(id) ? Important::Yes : Important::No);
set_property_inherited(id, style_for_revert.is_property_inherited(id) ? Inherited::Yes : Inherited::No);
if (property_affects_computed_font_list(id))
style().clear_computed_font_list_cache();
}
Display ComputedProperties::display_before_box_type_transformation() const
{
return m_display_before_box_type_transformation;
return data().display_before_box_type_transformation;
}
void ComputedProperties::set_display_before_box_type_transformation(Display value)
void ComputedProperties::Builder::set_display_before_box_type_transformation(Display value)
{
m_display_before_box_type_transformation = value;
data().display_before_box_type_transformation = value;
}
void ComputedProperties::set_animated_property(PropertyID id, NonnullRefPtr<StyleValue const> value, AnimatedPropertyResultOfTransition animated_property_result_of_transition, Inherited inherited)
void ComputedProperties::set_animated_property_internal(PropertyID id, NonnullRefPtr<StyleValue const> value, AnimatedPropertyResultOfTransition animated_property_result_of_transition, Inherited inherited)
{
m_animated_property_values.set(id, move(value));
set_animated_property_inherited(id, inherited);
set_animated_property_result_of_transition(id, animated_property_result_of_transition);
VERIFY(id >= first_longhand_property_id && id <= last_longhand_property_id);
mutable_animated_properties().set_property(id, move(value), animated_property_result_of_transition, inherited);
if (property_affects_computed_font_list(id))
clear_computed_font_list_cache();
}
void ComputedProperties::remove_animated_property(PropertyID id)
void ComputedProperties::set_animated_property(Badge<StyleComputer>, PropertyID id, NonnullRefPtr<StyleValue const> value, AnimatedPropertyResultOfTransition animated_property_result_of_transition, Inherited inherited)
{
m_animated_property_values.remove(id);
set_animated_property_internal(id, move(value), animated_property_result_of_transition, inherited);
}
void ComputedProperties::set_animated_property(Badge<DOM::Element>, PropertyID id, NonnullRefPtr<StyleValue const> value, AnimatedPropertyResultOfTransition animated_property_result_of_transition, Inherited inherited)
{
set_animated_property_internal(id, move(value), animated_property_result_of_transition, inherited);
}
void ComputedProperties::remove_animated_property(Badge<DOM::Element>, PropertyID id)
{
if (!has_animated_property(id))
return;
bool should_clear_computed_font_list_cache = property_affects_computed_font_list(id);
auto& animated_properties = mutable_animated_properties();
animated_properties.remove_property(id);
if (animated_properties.is_empty())
m_animated_properties = nullptr;
if (should_clear_computed_font_list_cache)
clear_computed_font_list_cache();
}
void ComputedProperties::reset_non_inherited_animated_properties(Badge<Animations::KeyframeEffect>)
{
for (auto property_id : m_animated_property_values.keys()) {
if (!is_animated_property_inherited(property_id))
m_animated_property_values.remove(property_id);
bool has_non_inherited_property = false;
bool should_clear_computed_font_list_cache = false;
for (auto const& property : animated_property_values()) {
if (is_animated_property_inherited(property.key))
continue;
has_non_inherited_property = true;
if (property_affects_computed_font_list(property.key)) {
should_clear_computed_font_list_cache = true;
break;
}
}
if (!has_non_inherited_property)
return;
auto& animated_properties = mutable_animated_properties();
animated_properties.reset_non_inherited_properties();
if (animated_properties.is_empty())
m_animated_properties = nullptr;
if (should_clear_computed_font_list_cache)
clear_computed_font_list_cache();
}
StyleValue const& ComputedProperties::property(PropertyID property_id, WithAnimationsApplied return_animated_value) const
@ -234,14 +411,14 @@ StyleValue const& ComputedProperties::property(PropertyID property_id, WithAnima
VERIFY(property_id >= first_longhand_property_id && property_id <= last_longhand_property_id);
// Important properties override animated but not transitioned properties
if (!m_animated_property_values.is_empty() && return_animated_value == WithAnimationsApplied::Yes
if (return_animated_value == WithAnimationsApplied::Yes
&& has_animated_property(property_id)
&& (!is_property_important(property_id) || is_animated_property_result_of_transition(property_id))) {
if (auto animated_value = m_animated_property_values.get(property_id); animated_value.has_value())
return *animated_value.value();
return animated_properties().property(property_id);
}
// By the time we call this method, the property should have been assigned
return *m_property_values[to_underlying(property_id) - to_underlying(first_longhand_property_id)];
return *data().property_values[to_underlying(property_id) - to_underlying(first_longhand_property_id)];
}
Variant<LengthPercentage, NormalGap> ComputedProperties::gap_value(PropertyID id) const
@ -1017,9 +1194,9 @@ Positioning ComputedProperties::position() const
bool ComputedProperties::operator==(ComputedProperties const& other) const
{
for (size_t i = 0; i < m_property_values.size(); ++i) {
auto const& my_style = m_property_values[i];
auto const& other_style = other.m_property_values[i];
for (size_t i = 0; i < data().property_values.size(); ++i) {
auto const& my_style = data().property_values[i];
auto const& other_style = other.data().property_values[i];
if (!my_style) {
if (other_style)
return false;
@ -2052,7 +2229,7 @@ Optional<FlyString> ComputedProperties::view_transition_name() const
return {};
}
Vector<ComputedProperties::AnimationProperties> ComputedProperties::animations(DOM::AbstractElement const& abstract_element) const
Vector<AnimationProperties> ComputedProperties::animations(DOM::AbstractElement const& abstract_element) const
{
auto const& animation_name_values = property(PropertyID::AnimationName).as_value_list().values();
@ -2435,7 +2612,7 @@ WillChange ComputedProperties::will_change() const
ValueComparingNonnullRefPtr<Gfx::FontCascadeList const> ComputedProperties::computed_font_list(FontComputer const& font_computer) const
{
if (!m_cached_computed_font_list) {
const_cast<ComputedProperties*>(this)->m_cached_computed_font_list = font_computer.compute_font_for_style_values(property(PropertyID::FontFamily), font_size(), font_slope(), font_weight(), font_width(), font_optical_sizing(), font_variation_settings(), font_feature_data());
m_cached_computed_font_list = font_computer.compute_font_for_style_values(property(PropertyID::FontFamily), font_size(), font_slope(), font_weight(), font_width(), font_optical_sizing(), font_variation_settings(), font_feature_data());
VERIFY(!m_cached_computed_font_list->is_empty());
}
@ -2447,7 +2624,7 @@ ValueComparingNonnullRefPtr<Gfx::Font const> ComputedProperties::first_available
if (!m_cached_first_available_computed_font) {
// https://drafts.csswg.org/css-fonts/#first-available-font
// First font for which the character U+0020 (space) is not excluded by a unicode-range
const_cast<ComputedProperties*>(this)->m_cached_first_available_computed_font = computed_font_list(font_computer)->font_for_code_point(' ');
m_cached_first_available_computed_font = computed_font_list(font_computer)->font_for_code_point(' ');
}
return *m_cached_first_available_computed_font;

View file

@ -7,6 +7,7 @@
#pragma once
#include <AK/FixedBitmap.h>
#include <AK/HashMap.h>
#include <AK/NonnullRefPtr.h>
#include <AK/RefCounted.h>
@ -14,6 +15,7 @@
#include <LibGfx/Font/Font.h>
#include <LibGfx/FontCascadeList.h>
#include <LibGfx/Forward.h>
#include <LibWeb/CSS/CSSAnimationProperties.h>
#include <LibWeb/CSS/ComputedValues.h>
#include <LibWeb/CSS/EasingFunction.h>
#include <LibWeb/CSS/FontFeatureData.h>
@ -27,6 +29,19 @@
namespace Web::CSS {
class AnimatedProperties;
class StyleComputer;
}
namespace Web::DOM {
class Element;
}
namespace Web::CSS {
struct TransitionProperties {
Vector<PropertyID> properties;
double duration;
@ -42,29 +57,89 @@ enum class AnimatedPropertyResultOfTransition : u8 {
class WEB_API ComputedProperties final : public RefCounted<ComputedProperties> {
public:
static NonnullRefPtr<ComputedProperties> create();
static constexpr double normal_line_height_scale = 1.15;
~ComputedProperties();
template<typename Callback>
inline void for_each_property(Callback callback) const
{
for (size_t i = 0; i < m_property_values.size(); ++i) {
if (m_property_values[i])
callback(static_cast<PropertyID>(i + to_underlying(first_longhand_property_id)), *m_property_values[i]);
}
}
void for_each_anchor_name(Function<void(FlyString const&)>) const;
enum class WithAnimationsApplied {
No,
Yes,
};
enum class Inherited {
No,
Yes
};
HashMap<PropertyID, NonnullRefPtr<StyleValue const>> const& animated_property_values() const { return m_animated_property_values; }
private:
class Data;
public:
class Builder {
public:
Builder();
ComputedProperties& style() { return *m_style; }
ComputedProperties const& style() const { return *m_style; }
NonnullRefPtr<ComputedProperties> build() &&;
bool depends_on_viewport_metrics() const { return m_depends_on_viewport_metrics; }
bool font_metrics_depend_on_viewport_metrics() const { return m_font_metrics_depend_on_viewport_metrics; }
Display display() const { return style().display(); }
StyleValue const& property(PropertyID property_id, WithAnimationsApplied with_animations_applied = WithAnimationsApplied::Yes) const { return style().property(property_id, with_animations_applied); }
[[nodiscard]] CSSPixels line_height() const { return style().line_height(); }
ValueComparingNonnullRefPtr<Gfx::Font const> first_available_computed_font(FontComputer const& font_computer) const { return style().first_available_computed_font(font_computer); }
void set_has_pseudo_element_styles(u64);
void set_property_important(PropertyID, Important);
void set_property_inherited(PropertyID, Inherited);
void set_depends_on_viewport_metrics();
void set_font_metrics_depend_on_viewport_metrics();
void set_has_pseudo_element_style(PseudoElement);
void set_property(PropertyID, NonnullRefPtr<StyleValue const> value, Inherited = Inherited::No, Important = Important::No);
void set_property_without_modifying_flags(PropertyID, NonnullRefPtr<StyleValue const> value);
void revert_property(PropertyID, ComputedProperties const& style_for_revert);
void set_display_before_box_type_transformation(Display);
HashMap<PropertyID, NonnullRefPtr<StyleValue const>> const& inheritance_dependent_specified_values() const { return m_data->inheritance_dependent_specified_values; }
void add_inheritance_dependent_specified_value(PropertyID property_id, NonnullRefPtr<StyleValue const> value) { m_data->inheritance_dependent_specified_values.set(property_id, move(value)); }
RefPtr<StyleValue const> raw_cascaded_font_size() const { return m_data->raw_cascaded_font_size; }
void set_raw_cascaded_font_size(NonnullRefPtr<StyleValue const> value) { m_data->raw_cascaded_font_size = move(value); }
private:
friend class ComputedProperties;
Builder(ComputedProperties const&);
Data& data() { return *m_data; }
Data const& data() const { return *m_data; }
NonnullRefPtr<Data> m_data;
NonnullRefPtr<ComputedProperties> m_style;
bool m_depends_on_viewport_metrics { false };
bool m_font_metrics_depend_on_viewport_metrics { false };
};
static NonnullRefPtr<ComputedProperties> create(Builder&&);
static Builder create_builder();
static Builder create_builder_with_base_values_from(ComputedProperties const&);
template<typename Callback>
inline void for_each_property(Callback callback) const
{
for (size_t i = 0; i < m_data->property_values.size(); ++i) {
if (m_data->property_values[i])
callback(static_cast<PropertyID>(i + to_underlying(first_longhand_property_id)), *m_data->property_values[i]);
}
}
void for_each_anchor_name(Function<void(FlyString const&)>) const;
HashMap<PropertyID, NonnullRefPtr<StyleValue const>> const& animated_property_values() const;
RefPtr<AnimatedProperties const> animated_properties_snapshot() const;
bool has_animated_property(PropertyID property_id) const;
void reset_non_inherited_animated_properties(Badge<Animations::KeyframeEffect>);
bool is_property_important(PropertyID property_id) const;
@ -74,25 +149,12 @@ public:
bool depends_on_viewport_metrics() const { return m_depends_on_viewport_metrics; }
bool font_metrics_depend_on_viewport_metrics() const { return m_font_metrics_depend_on_viewport_metrics; }
bool has_pseudo_element_style(PseudoElement) const;
void set_has_pseudo_element_styles(u64);
void set_property_important(PropertyID, Important);
void set_property_inherited(PropertyID, Inherited);
void set_animated_property_inherited(PropertyID, Inherited);
void set_animated_property_result_of_transition(PropertyID, AnimatedPropertyResultOfTransition);
void set_depends_on_viewport_metrics() { m_depends_on_viewport_metrics = true; }
void set_font_metrics_depend_on_viewport_metrics() { m_font_metrics_depend_on_viewport_metrics = true; }
void set_has_pseudo_element_style(PseudoElement);
void set_property(PropertyID, NonnullRefPtr<StyleValue const> value, Inherited = Inherited::No, Important = Important::No);
void set_property_without_modifying_flags(PropertyID, NonnullRefPtr<StyleValue const> value);
void set_animated_property(PropertyID, NonnullRefPtr<StyleValue const> value, AnimatedPropertyResultOfTransition, Inherited = Inherited::No);
void remove_animated_property(PropertyID);
enum class WithAnimationsApplied {
No,
Yes,
};
void set_depends_on_viewport_metrics(Badge<StyleComputer>);
void set_font_metrics_depend_on_viewport_metrics(Badge<StyleComputer>);
void set_animated_property(Badge<StyleComputer>, PropertyID, NonnullRefPtr<StyleValue const> value, AnimatedPropertyResultOfTransition, Inherited = Inherited::No);
void set_animated_property(Badge<DOM::Element>, PropertyID, NonnullRefPtr<StyleValue const> value, AnimatedPropertyResultOfTransition, Inherited = Inherited::No);
void remove_animated_property(Badge<DOM::Element>, PropertyID);
StyleValue const& property(PropertyID, WithAnimationsApplied = WithAnimationsApplied::Yes) const;
void revert_property(PropertyID, ComputedProperties const& style_for_revert);
Size size_value(PropertyID) const;
[[nodiscard]] Variant<LengthPercentage, NormalGap> gap_value(PropertyID) const;
@ -210,23 +272,10 @@ public:
ContainerType container_type() const;
MixBlendMode mix_blend_mode() const;
Optional<FlyString> view_transition_name() const;
struct AnimationProperties {
Variant<double, String> duration;
EasingFunction timing_function;
double iteration_count;
AnimationDirection direction;
AnimationPlayState play_state;
double delay;
AnimationFillMode fill_mode;
AnimationComposition composition;
FlyString name;
GC::Ptr<Animations::AnimationTimeline> timeline;
};
Vector<AnimationProperties> animations(DOM::AbstractElement const&) const;
Vector<TransitionProperties> transitions() const;
Display display_before_box_type_transformation() const;
void set_display_before_box_type_transformation(Display value);
static Vector<NonnullRefPtr<TransformationStyleValue const>> transformations_for_style_value(StyleValue const& value);
Vector<NonnullRefPtr<TransformationStyleValue const>> transformations() const;
@ -284,44 +333,80 @@ public:
static NonnullRefPtr<Gfx::Font const> font_fallback(bool monospace, bool bold, float point_size);
HashMap<PropertyID, NonnullRefPtr<StyleValue const>> const& inheritance_dependent_specified_values() const { return m_inheritance_dependent_specified_values; }
void add_inheritance_dependent_specified_value(PropertyID property_id, NonnullRefPtr<StyleValue const> value) { m_inheritance_dependent_specified_values.set(property_id, move(value)); }
HashMap<PropertyID, NonnullRefPtr<StyleValue const>> const& inheritance_dependent_specified_values() const { return m_data->inheritance_dependent_specified_values; }
RefPtr<StyleValue const> raw_cascaded_font_size() const { return m_raw_cascaded_font_size; }
void set_raw_cascaded_font_size(NonnullRefPtr<StyleValue const> value) { m_raw_cascaded_font_size = move(value); }
RefPtr<StyleValue const> raw_cascaded_font_size() const { return m_data->raw_cascaded_font_size; }
private:
ComputedProperties();
class Data final : public RefCounted<Data> {
public:
Data() = default;
Array<RefPtr<StyleValue const>, number_of_longhand_properties> property_values;
AK::FixedBitmap<number_of_longhand_properties> property_important { false };
AK::FixedBitmap<number_of_longhand_properties> property_inherited { false };
Display display_before_box_type_transformation { InitialValues::display() };
u64 pseudo_element_styles { 0 };
Optional<CSSPixels> line_height;
HashMap<PropertyID, NonnullRefPtr<StyleValue const>> inheritance_dependent_specified_values;
RefPtr<StyleValue const> raw_cascaded_font_size;
};
ComputedProperties(NonnullRefPtr<Data const>, bool depends_on_viewport_metrics, bool font_metrics_depend_on_viewport_metrics);
Overflow overflow(PropertyID) const;
Vector<ShadowData> shadow(PropertyID, Layout::Node const&) const;
Position position_value(PropertyID) const;
Array<RefPtr<StyleValue const>, number_of_longhand_properties> m_property_values;
Array<u8, ceil_div(number_of_longhand_properties, 8uz)> m_property_important {};
Array<u8, ceil_div(number_of_longhand_properties, 8uz)> m_property_inherited {};
Array<u8, ceil_div(number_of_longhand_properties, 8uz)> m_animated_property_inherited {};
Array<u8, ceil_div(number_of_longhand_properties, 8uz)> m_animated_property_result_of_transition {};
Data const& data() const { return *m_data; }
AnimatedProperties const& animated_properties() const;
AnimatedProperties& mutable_animated_properties();
void set_animated_property_internal(PropertyID, NonnullRefPtr<StyleValue const>, AnimatedPropertyResultOfTransition, Inherited);
HashMap<PropertyID, NonnullRefPtr<StyleValue const>> m_animated_property_values;
Display m_display_before_box_type_transformation { InitialValues::display() };
NonnullRefPtr<Data const> m_data;
RefPtr<AnimatedProperties> m_animated_properties;
bool m_depends_on_viewport_metrics { false };
bool m_font_metrics_depend_on_viewport_metrics { false };
u64 m_pseudo_element_styles { 0 };
RefPtr<Gfx::FontCascadeList const> m_cached_computed_font_list;
RefPtr<Gfx::Font const> m_cached_first_available_computed_font;
mutable RefPtr<Gfx::FontCascadeList const> m_cached_computed_font_list;
mutable RefPtr<Gfx::Font const> m_cached_first_available_computed_font;
void clear_computed_font_list_cache()
{
m_cached_computed_font_list = nullptr;
m_cached_first_available_computed_font = nullptr;
}
};
Optional<CSSPixels> m_line_height;
class AnimatedProperties final : public RefCounted<AnimatedProperties> {
public:
using PropertyMap = HashMap<PropertyID, NonnullRefPtr<StyleValue const>>;
HashMap<PropertyID, NonnullRefPtr<StyleValue const>> m_inheritance_dependent_specified_values;
RefPtr<StyleValue const> m_raw_cascaded_font_size;
AnimatedProperties() = default;
AnimatedProperties(AnimatedProperties const&);
bool is_empty() const { return m_values.is_empty(); }
PropertyMap const& values() const { return m_values; }
bool has_property(PropertyID) const;
bool is_property_inherited(PropertyID) const;
bool is_property_result_of_transition(PropertyID) const;
StyleValue const& property(PropertyID) const;
void set_property(PropertyID, NonnullRefPtr<StyleValue const>, AnimatedPropertyResultOfTransition, ComputedProperties::Inherited);
void remove_property(PropertyID);
void reset_non_inherited_properties();
private:
void set_property_inherited(PropertyID, ComputedProperties::Inherited);
void set_property_result_of_transition(PropertyID, AnimatedPropertyResultOfTransition);
AK::FixedBitmap<number_of_longhand_properties> m_has_property { false };
AK::FixedBitmap<number_of_longhand_properties> m_property_inherited { false };
AK::FixedBitmap<number_of_longhand_properties> m_property_result_of_transition { false };
PropertyMap m_values;
};
}

View file

@ -1006,7 +1006,17 @@ static Optional<CSS::EasingFunction> resolve_keyframe_easing(CSS::StyleValue con
return {};
}
void StyleComputer::collect_animation_into(DOM::AbstractElement abstract_element, GC::Ref<Animations::KeyframeEffect> effect, ComputedProperties::Builder& builder) const
{
collect_animation_into(abstract_element, effect, builder.style(), &builder);
}
void StyleComputer::collect_animation_into(DOM::AbstractElement abstract_element, GC::Ref<Animations::KeyframeEffect> effect, ComputedProperties& computed_properties) const
{
collect_animation_into(abstract_element, effect, computed_properties, nullptr);
}
void StyleComputer::collect_animation_into(DOM::AbstractElement abstract_element, GC::Ref<Animations::KeyframeEffect> effect, ComputedProperties& computed_properties, ComputedProperties::Builder* builder) const
{
auto animation = effect->associated_animation();
if (!animation)
@ -1083,7 +1093,7 @@ void StyleComputer::collect_animation_into(DOM::AbstractElement abstract_element
}
// FIXME: Follow https://drafts.csswg.org/web-animations-1/#ref-for-computed-keyframes in whatever the right place is.
auto compute_keyframe_values = [&computed_properties, &abstract_element, this](auto const& keyframe_values) {
auto compute_keyframe_values = [&computed_properties, &abstract_element, builder, this](auto const& keyframe_values) {
HashMap<PropertyID, RefPtr<StyleValue const>> result;
HashMap<PropertyID, PropertyID> longhands_set_by_property_id;
AK::FixedBitmap<number_of_longhand_properties> property_is_set_by_use_initial(false);
@ -1215,9 +1225,15 @@ void StyleComputer::collect_animation_into(DOM::AbstractElement abstract_element
computation_context.reset_viewport_metric_dependency_tracking();
result.set(property_id, compute_value_of_property(property_id, *style_value, get_property_specified_value, computation_context, m_document->page().client().device_pixels_per_css_pixel()));
if (computation_context.depends_on_viewport_metrics()) {
computed_properties.set_depends_on_viewport_metrics();
if (property_affects_font_metrics(property_id))
computed_properties.set_font_metrics_depend_on_viewport_metrics();
if (builder) {
builder->set_depends_on_viewport_metrics();
if (property_affects_font_metrics(property_id))
builder->set_font_metrics_depend_on_viewport_metrics();
} else {
computed_properties.set_depends_on_viewport_metrics(Badge<StyleComputer> {});
if (property_affects_font_metrics(property_id))
computed_properties.set_font_metrics_depend_on_viewport_metrics(Badge<StyleComputer> {});
}
}
}
@ -1253,7 +1269,7 @@ void StyleComputer::collect_animation_into(DOM::AbstractElement abstract_element
if (!resolved_end_property) {
if (resolved_start_property) {
computed_properties.set_animated_property(it.key, *resolved_start_property, is_result_of_transition);
computed_properties.set_animated_property(Badge<StyleComputer> {}, it.key, *resolved_start_property, is_result_of_transition);
dbgln_if(LIBWEB_CSS_ANIMATION_DEBUG, "No end property for property {}, using {}", string_from_property_id(it.key), resolved_start_property->to_string(SerializationMode::Normal));
}
continue;
@ -1283,11 +1299,11 @@ void StyleComputer::collect_animation_into(DOM::AbstractElement abstract_element
if (auto next_value = interpolate_property(*effect->target(), it.key, *start, *end, progress_in_keyframe, AllowDiscrete::Yes)) {
dbgln_if(LIBWEB_CSS_ANIMATION_DEBUG, "Interpolated value for property {} at {}: {} -> {} = {}", string_from_property_id(it.key), progress_in_keyframe, start->to_string(SerializationMode::Normal), end->to_string(SerializationMode::Normal), next_value->to_string(SerializationMode::Normal));
computed_properties.set_animated_property(it.key, *next_value, is_result_of_transition);
computed_properties.set_animated_property(Badge<StyleComputer> {}, it.key, *next_value, is_result_of_transition);
} else {
// If interpolate_property() fails, the element should not be rendered
dbgln_if(LIBWEB_CSS_ANIMATION_DEBUG, "Interpolated value for property {} at {}: {} -> {} is invalid", string_from_property_id(it.key), progress_in_keyframe, start->to_string(SerializationMode::Normal), end->to_string(SerializationMode::Normal));
computed_properties.set_animated_property(PropertyID::Visibility, KeywordStyleValue::create(Keyword::Hidden), is_result_of_transition);
computed_properties.set_animated_property(Badge<StyleComputer> {}, PropertyID::Visibility, KeywordStyleValue::create(Keyword::Hidden), is_result_of_transition);
}
}
}
@ -1431,8 +1447,10 @@ static void compute_transitioned_properties(ComputedProperties const& style, DOM
}
// https://drafts.csswg.org/css-transitions/#starting
void StyleComputer::start_needed_transitions(ComputedProperties const& previous_style, ComputedProperties& new_style, DOM::AbstractElement abstract_element) const
void StyleComputer::start_needed_transitions(ComputedProperties const& previous_style, ComputedProperties::Builder& new_style_builder, DOM::AbstractElement abstract_element) const
{
auto& new_style = new_style_builder.style();
// https://drafts.csswg.org/css-transitions/#transition-combined-duration
auto combined_duration = [](Animations::Animatable::TransitionAttributes const& transition_attributes) {
// Define the combined duration of the transition as the sum of max(matching transition duration, 0s) and the matching transition delay.
@ -1468,7 +1486,7 @@ void StyleComputer::start_needed_transitions(ComputedProperties const& previous_
auto transition = CSSTransition::start_a_transition(abstract_element, property_id,
document().transition_generation(), delay, start_time, end_time, start_value, end_value, reversing_adjusted_start_value, reversing_shortening_factor);
// Immediately set the property's value to the transition's current value, to prevent single-frame jumps.
collect_animation_into(abstract_element, as<Animations::KeyframeEffect>(*transition->effect()), new_style);
collect_animation_into(abstract_element, as<Animations::KeyframeEffect>(*transition->effect()), new_style_builder);
};
// 1. If all of the following are true:
@ -2257,6 +2275,9 @@ Optional<StyleComputer::AnimatedInheritValue> StyleComputer::get_animated_inheri
if (!parent_element.has_value() || !parent_element->computed_properties())
return {};
if (!parent_element->computed_properties()->has_animated_property(property_id))
return {};
if (auto animated_value = parent_element->computed_properties()->animated_property_values().get(property_id); animated_value.has_value())
return AnimatedInheritValue {
.value = *animated_value.value(),
@ -2349,9 +2370,10 @@ static bool line_height_value_depends_on_computed_font_size(StyleValue const& va
|| (value.is_calculated() && value.as_calculated().contains_percentage());
}
void StyleComputer::compute_property_values(ComputedProperties& style, Optional<DOM::AbstractElement> abstract_element) const
void StyleComputer::compute_property_values(ComputedProperties::Builder& builder, Optional<DOM::AbstractElement> abstract_element) const
{
VERIFY(computation_context_cache_is_empty());
auto& style = builder.style();
// NOTE: This doesn't necessarily return the specified value if we have already computed this property but that
// doesn't matter as a computed value is always valid as a specified value.
Function<NonnullRefPtr<StyleValue const>(PropertyID)> const get_property_specified_value = [&](auto property_id) -> NonnullRefPtr<StyleValue const> {
@ -2367,19 +2389,19 @@ void StyleComputer::compute_property_values(ComputedProperties& style, Optional<
computation_context.reset_viewport_metric_dependency_tracking();
auto const& computed_value = compute_value_of_property(property_id, specified_value, get_property_specified_value, computation_context, device_pixels_per_css_pixel);
if (computation_context.depends_on_viewport_metrics()) {
style.set_depends_on_viewport_metrics();
builder.set_depends_on_viewport_metrics();
if (property_affects_font_metrics(property_id))
style.set_font_metrics_depend_on_viewport_metrics();
builder.set_font_metrics_depend_on_viewport_metrics();
}
style.set_property_without_modifying_flags(property_id, computed_value);
builder.set_property_without_modifying_flags(property_id, computed_value);
}
clear_computation_context_caches();
if (abstract_element.has_value() && is<HTML::HTMLHtmlElement>(abstract_element->element())) {
m_root_element_font_metrics = calculate_root_element_font_metrics(style);
m_root_element_font_metrics_depend_on_viewport_metrics = style.font_metrics_depend_on_viewport_metrics();
m_root_element_font_metrics_depend_on_viewport_metrics = builder.font_metrics_depend_on_viewport_metrics();
}
}
@ -2490,8 +2512,9 @@ ComputationContext const& StyleComputer::get_computation_context_for_property(Pr
VERIFY_NOT_REACHED();
}
void StyleComputer::resolve_effective_overflow_values(ComputedProperties& style) const
void StyleComputer::resolve_effective_overflow_values(ComputedProperties::Builder& builder) const
{
auto& style = builder.style();
// https://www.w3.org/TR/css-overflow-3/#overflow-control
// The visible/clip values of overflow compute to auto/hidden (respectively) if one of overflow-x or
// overflow-y is neither visible nor clip.
@ -2501,18 +2524,19 @@ void StyleComputer::resolve_effective_overflow_values(ComputedProperties& style)
auto overflow_y_is_visible_or_clip = overflow_y == Overflow::Visible || overflow_y == Overflow::Clip;
if (!overflow_x_is_visible_or_clip || !overflow_y_is_visible_or_clip) {
if (overflow_x == CSS::Overflow::Visible)
style.set_property(CSS::PropertyID::OverflowX, KeywordStyleValue::create(Keyword::Auto));
builder.set_property(CSS::PropertyID::OverflowX, KeywordStyleValue::create(Keyword::Auto));
if (overflow_x == CSS::Overflow::Clip)
style.set_property(CSS::PropertyID::OverflowX, KeywordStyleValue::create(Keyword::Hidden));
builder.set_property(CSS::PropertyID::OverflowX, KeywordStyleValue::create(Keyword::Hidden));
if (overflow_y == CSS::Overflow::Visible)
style.set_property(CSS::PropertyID::OverflowY, KeywordStyleValue::create(Keyword::Auto));
builder.set_property(CSS::PropertyID::OverflowY, KeywordStyleValue::create(Keyword::Auto));
if (overflow_y == CSS::Overflow::Clip)
style.set_property(CSS::PropertyID::OverflowY, KeywordStyleValue::create(Keyword::Hidden));
builder.set_property(CSS::PropertyID::OverflowY, KeywordStyleValue::create(Keyword::Hidden));
}
}
static void compute_text_align(ComputedProperties& style, DOM::AbstractElement abstract_element)
static void compute_text_align(ComputedProperties::Builder& builder, DOM::AbstractElement abstract_element)
{
auto& style = builder.style();
auto text_align_keyword = style.property(PropertyID::TextAlign).to_keyword();
// https://drafts.csswg.org/css-text-4/#valdef-text-align-match-parent
@ -2526,25 +2550,25 @@ static void compute_text_align(ComputedProperties& style, DOM::AbstractElement a
switch (parent_text_align.to_keyword()) {
case Keyword::Start:
if (parent_direction == Direction::Ltr) {
style.set_property(PropertyID::TextAlign, KeywordStyleValue::create(Keyword::Left));
builder.set_property(PropertyID::TextAlign, KeywordStyleValue::create(Keyword::Left));
} else {
style.set_property(PropertyID::TextAlign, KeywordStyleValue::create(Keyword::Right));
builder.set_property(PropertyID::TextAlign, KeywordStyleValue::create(Keyword::Right));
}
break;
case Keyword::End:
if (parent_direction == Direction::Ltr) {
style.set_property(PropertyID::TextAlign, KeywordStyleValue::create(Keyword::Right));
builder.set_property(PropertyID::TextAlign, KeywordStyleValue::create(Keyword::Right));
} else {
style.set_property(PropertyID::TextAlign, KeywordStyleValue::create(Keyword::Left));
builder.set_property(PropertyID::TextAlign, KeywordStyleValue::create(Keyword::Left));
}
break;
default:
style.set_property(PropertyID::TextAlign, parent_text_align);
builder.set_property(PropertyID::TextAlign, parent_text_align);
}
} else {
style.set_property(PropertyID::TextAlign, KeywordStyleValue::create(Keyword::Start));
builder.set_property(PropertyID::TextAlign, KeywordStyleValue::create(Keyword::Start));
}
}
@ -2555,11 +2579,11 @@ static void compute_text_align(ComputedProperties& style, DOM::AbstractElement a
if (parent_element.has_value() && parent_element->computed_properties()) {
auto const& parent_text_align = parent_element->computed_properties()->property(PropertyID::TextAlign);
if (parent_text_align.to_keyword() != Keyword::Start) {
style.set_property(PropertyID::TextAlign, parent_text_align, ComputedProperties::Inherited::Yes);
builder.set_property(PropertyID::TextAlign, parent_text_align, ComputedProperties::Inherited::Yes);
return;
}
}
style.set_property(PropertyID::TextAlign, KeywordStyleValue::create(Keyword::Center));
builder.set_property(PropertyID::TextAlign, KeywordStyleValue::create(Keyword::Center));
}
}
@ -2601,8 +2625,9 @@ static BoxTypeTransformation required_box_type_transformation(ComputedProperties
}
// https://drafts.csswg.org/css-display/#transformations
void StyleComputer::transform_box_type_if_needed(ComputedProperties& style, DOM::AbstractElement abstract_element) const
void StyleComputer::transform_box_type_if_needed(ComputedProperties::Builder& builder, DOM::AbstractElement abstract_element) const
{
auto& style = builder.style();
// 2.7. Automatic Box Type Transformations
// Some layout effects require blockification or inlinification of the box type,
@ -2611,7 +2636,7 @@ void StyleComputer::transform_box_type_if_needed(ComputedProperties& style, DOM:
auto display = style.display();
style.set_display_before_box_type_transformation(display);
builder.set_display_before_box_type_transformation(display);
if (display.is_none() || (display.is_contents() && !abstract_element.element().is_document_element()))
return;
@ -2619,7 +2644,7 @@ void StyleComputer::transform_box_type_if_needed(ComputedProperties& style, DOM:
// https://drafts.csswg.org/css-display/#root
// The root elements display type is always blockified, and its principal box always establishes an independent formatting context.
if (abstract_element.element().is_document_element() && !display.is_block_outside()) {
style.set_property(PropertyID::Display, DisplayStyleValue::create(Display::from_short(Display::Short::Block)));
builder.set_property(PropertyID::Display, DisplayStyleValue::create(Display::from_short(Display::Short::Block)));
return;
}
@ -2645,7 +2670,7 @@ void StyleComputer::transform_box_type_if_needed(ComputedProperties& style, DOM:
// https://www.w3.org/TR/CSS2/visuren.html#dis-pos-flo
// If 'position' has the value 'absolute' or 'fixed', [...] 'float' is set to 'none'
if (style.position() == Positioning::Absolute || style.position() == Positioning::Fixed)
style.set_property(PropertyID::Float, KeywordStyleValue::create(Keyword::None));
builder.set_property(PropertyID::Float, KeywordStyleValue::create(Keyword::None));
switch (required_box_type_transformation(style, abstract_element)) {
case BoxTypeTransformation::None:
@ -2694,22 +2719,22 @@ void StyleComputer::transform_box_type_if_needed(ComputedProperties& style, DOM:
}
if (new_display != display)
style.set_property(PropertyID::Display, DisplayStyleValue::create(new_display));
builder.set_property(PropertyID::Display, DisplayStyleValue::create(new_display));
}
NonnullRefPtr<ComputedProperties> StyleComputer::create_document_style() const
{
auto style = CSS::ComputedProperties::create();
auto builder = CSS::ComputedProperties::create_builder();
for (auto i = to_underlying(CSS::first_longhand_property_id); i <= to_underlying(CSS::last_longhand_property_id); ++i) {
auto property_id = static_cast<PropertyID>(i);
style->set_property(property_id, property_initial_value(property_id));
builder.set_property(property_id, property_initial_value(property_id));
}
compute_property_values(style, {});
style->set_property(CSS::PropertyID::Width, CSS::LengthStyleValue::create(CSS::Length::make_px(viewport_rect().width())));
style->set_property(CSS::PropertyID::Height, CSS::LengthStyleValue::create(CSS::Length::make_px(viewport_rect().height())));
style->set_property(CSS::PropertyID::Display, CSS::DisplayStyleValue::create(CSS::Display::from_short(CSS::Display::Short::Block)));
return style;
compute_property_values(builder, {});
builder.set_property(CSS::PropertyID::Width, CSS::LengthStyleValue::create(CSS::Length::make_px(viewport_rect().width())));
builder.set_property(CSS::PropertyID::Height, CSS::LengthStyleValue::create(CSS::Length::make_px(viewport_rect().height())));
builder.set_property(CSS::PropertyID::Display, CSS::DisplayStyleValue::create(CSS::Display::from_short(CSS::Display::Short::Block)));
return CSS::ComputedProperties::create(move(builder));
}
NonnullRefPtr<ComputedProperties> StyleComputer::compute_style(DOM::AbstractElement abstract_element, Optional<bool&> did_change_custom_properties) const
@ -2762,10 +2787,11 @@ RefPtr<ComputedProperties> StyleComputer::compute_style_impl(DOM::AbstractElemen
else
abstract_element_for_pseudo_element.set_inheritance_override(host_element);
auto style = compute_style(abstract_element_for_pseudo_element);
auto inherited_pseudo_element_style = compute_style(abstract_element_for_pseudo_element);
auto builder = ComputedProperties::create_builder_with_base_values_from(*inherited_pseudo_element_style);
abstract_element.element().adjust_computed_style(style);
return style;
abstract_element.element().adjust_computed_style(builder);
return ComputedProperties::create(move(builder));
}
ScopeGuard guard { [&abstract_element]() { abstract_element.element().set_needs_style_update(false); } };
@ -2871,8 +2897,7 @@ RefPtr<ComputedProperties> StyleComputer::compute_style_impl(DOM::AbstractElemen
}
}
auto computed_properties = compute_properties(abstract_element, cascaded_properties);
computed_properties->set_has_pseudo_element_styles(matching_rule_set.matching_pseudo_element_styles);
auto computed_properties = compute_properties(abstract_element, cascaded_properties, matching_rule_set.matching_pseudo_element_styles);
if (did_change_custom_properties.has_value()) {
auto new_custom_property_data = abstract_element.custom_property_data();
@ -3001,32 +3026,34 @@ RefPtr<StyleValue const> StyleComputer::recascade_font_size_if_needed(DOM::Abstr
return CSS::LengthStyleValue::create(CSS::Length::make_px(current_size_in_px));
}
NonnullRefPtr<ComputedProperties> StyleComputer::compute_properties(DOM::AbstractElement abstract_element, CascadedProperties& cascaded_properties) const
NonnullRefPtr<ComputedProperties> StyleComputer::compute_properties(DOM::AbstractElement abstract_element, CascadedProperties& cascaded_properties, u64 matching_pseudo_element_styles) const
{
VERIFY(computation_context_cache_is_empty());
auto computed_style = CSS::ComputedProperties::create();
auto builder = CSS::ComputedProperties::create_builder();
auto& computed_style = builder.style();
builder.set_has_pseudo_element_styles(matching_pseudo_element_styles);
bool recascaded_font_size_depends_on_viewport_metrics = false;
auto new_font_size = recascade_font_size_if_needed(abstract_element, cascaded_properties, recascaded_font_size_depends_on_viewport_metrics);
if (new_font_size) {
computed_style->set_property(PropertyID::FontSize, *new_font_size, ComputedProperties::Inherited::No, Important::No);
builder.set_property(PropertyID::FontSize, *new_font_size, ComputedProperties::Inherited::No, Important::No);
if (recascaded_font_size_depends_on_viewport_metrics) {
computed_style->set_depends_on_viewport_metrics();
computed_style->set_font_metrics_depend_on_viewport_metrics();
builder.set_depends_on_viewport_metrics();
builder.set_font_metrics_depend_on_viewport_metrics();
}
}
auto const& computed_properties_to_inherit_from = abstract_element.element_to_inherit_style_from().map([](auto const& element) { return element.computed_properties(); }).value_or(nullptr);
Function<NonnullRefPtr<StyleValue const>(PropertyID)> const get_property_specified_value = [&](auto property_id) -> NonnullRefPtr<StyleValue const> {
return computed_style->property(property_id);
return computed_style.property(property_id);
};
auto const device_pixels_per_css_pixel = m_document->page().client().device_pixels_per_css_pixel();
auto const compute_property = [&](PropertyID property_id, NonnullRefPtr<StyleValue const> const& style_value, bool& depends_on_viewport_metrics) {
auto const& computation_context = get_computation_context_for_property(property_id, *computed_style, abstract_element);
auto const& computation_context = get_computation_context_for_property(property_id, computed_style, abstract_element);
computation_context.reset_viewport_metric_dependency_tracking();
auto computed_value = compute_value_of_property(property_id, style_value, get_property_specified_value, computation_context, device_pixels_per_css_pixel);
if (computation_context.depends_on_viewport_metrics())
@ -3037,7 +3064,7 @@ NonnullRefPtr<ComputedProperties> StyleComputer::compute_properties(DOM::Abstrac
Optional<LogicalAliasMappingContext> logical_alias_mapping_context;
auto const get_logical_alias_mapping_context = [&]() {
if (!logical_alias_mapping_context.has_value())
logical_alias_mapping_context = LogicalAliasMappingContext { computed_style->writing_mode(), computed_style->direction() };
logical_alias_mapping_context = LogicalAliasMappingContext { computed_style.writing_mode(), computed_style.direction() };
return *logical_alias_mapping_context;
};
@ -3074,7 +3101,7 @@ NonnullRefPtr<ComputedProperties> StyleComputer::compute_properties(DOM::Abstrac
if (auto cascaded_style_property = cascaded_properties.style_property(cascaded_property_id); cascaded_style_property.has_value()) {
if (cascaded_style_property->important == Important::Yes)
computed_style->set_property_important(property_id, Important::Yes);
builder.set_property_important(property_id, Important::Yes);
value = cascaded_style_property->value;
requires_computation = property_requires_computation_with_cascaded_value(property_id);
@ -3082,7 +3109,7 @@ NonnullRefPtr<ComputedProperties> StyleComputer::compute_properties(DOM::Abstrac
// font-size when font-family is monospace.
// See the recascade_font_size_if_needed() function for further details.
if (property_id == PropertyID::FontSize)
computed_style->set_raw_cascaded_font_size(*cascaded_style_property->value);
builder.set_raw_cascaded_font_size(*cascaded_style_property->value);
}
// NOTE: We've already handled font-size above.
@ -3109,21 +3136,25 @@ NonnullRefPtr<ComputedProperties> StyleComputer::compute_properties(DOM::Abstrac
if (auto* parent = abstract_element.element().parent(); parent && is<DOM::ShadowRoot>(*parent))
parent->set_children_may_depend_on_non_inherited_property_inheritance();
}
computed_style->set_property_inherited(property_id, ComputedProperties::Inherited::Yes);
builder.set_property_inherited(property_id, ComputedProperties::Inherited::Yes);
value = computed_properties_to_inherit_from->property(inherited_property_id, ComputedProperties::WithAnimationsApplied::No);
requires_computation = property_requires_computation_with_inherited_value(property_id);
if (property_affects_font_metrics(inherited_property_id) && computed_properties_to_inherit_from->font_metrics_depend_on_viewport_metrics())
computed_style->set_font_metrics_depend_on_viewport_metrics();
builder.set_font_metrics_depend_on_viewport_metrics();
// FIXME: Do we need to recompute animated inherited values?
if (auto animated_value = computed_properties_to_inherit_from->animated_property_values().get(inherited_property_id); animated_value.has_value())
computed_style->set_animated_property(
if (computed_properties_to_inherit_from->has_animated_property(inherited_property_id)) {
auto animated_value = computed_properties_to_inherit_from->animated_property_values().get(inherited_property_id);
VERIFY(animated_value.has_value());
computed_style.set_animated_property(
Badge<StyleComputer> {},
property_id,
*animated_value.value(),
computed_properties_to_inherit_from->is_animated_property_result_of_transition(inherited_property_id)
? AnimatedPropertyResultOfTransition::Yes
: AnimatedPropertyResultOfTransition::No,
ComputedProperties::Inherited::Yes);
}
}
if (!value || value->is_initial() || value->is_unset() || (should_inherit && !computed_properties_to_inherit_from)) {
@ -3138,7 +3169,7 @@ NonnullRefPtr<ComputedProperties> StyleComputer::compute_properties(DOM::Abstrac
|| (property_id == PropertyID::FontSize && font_size_value_depends_on_inherited_font_size(*value))
|| (property_id == PropertyID::LineHeight && line_height_value_depends_on_computed_font_size(*value));
if (depends_on_inherited_info)
computed_style->add_inheritance_dependent_specified_value(property_id, *value);
builder.add_inheritance_dependent_specified_value(property_id, *value);
// NB: We compute using the inherited (physical) property to avoid having to add cases for all the logical
// alias properties in `compute_value_of_property`
@ -3147,16 +3178,16 @@ NonnullRefPtr<ComputedProperties> StyleComputer::compute_properties(DOM::Abstrac
? compute_property(inherited_property_id, value.release_nonnull(), depends_on_viewport_metrics)
: value.release_nonnull();
if (depends_on_viewport_metrics) {
computed_style->set_depends_on_viewport_metrics();
builder.set_depends_on_viewport_metrics();
if (property_affects_font_metrics(inherited_property_id))
computed_style->set_font_metrics_depend_on_viewport_metrics();
builder.set_font_metrics_depend_on_viewport_metrics();
}
computed_style->set_property_without_modifying_flags(property_id, move(computed_value));
builder.set_property_without_modifying_flags(property_id, move(computed_value));
}
if (is<HTML::HTMLHtmlElement>(abstract_element.element())) {
m_root_element_font_metrics = calculate_root_element_font_metrics(computed_style);
m_root_element_font_metrics_depend_on_viewport_metrics = computed_style->font_metrics_depend_on_viewport_metrics();
m_root_element_font_metrics_depend_on_viewport_metrics = builder.font_metrics_depend_on_viewport_metrics();
}
// Compute the value of custom properties
@ -3177,30 +3208,30 @@ NonnullRefPtr<ComputedProperties> StyleComputer::compute_properties(DOM::Abstrac
if (auto effect = animation->effect(); effect && effect->is_keyframe_effect()) {
auto& keyframe_effect = *static_cast<Animations::KeyframeEffect*>(effect.ptr());
if (keyframe_effect.pseudo_element_type() == abstract_element.pseudo_element())
collect_animation_into(abstract_element, keyframe_effect, computed_style);
collect_animation_into(abstract_element, keyframe_effect, builder);
}
}
}
// Run automatic box type transformations
transform_box_type_if_needed(computed_style, abstract_element);
transform_box_type_if_needed(builder, abstract_element);
// Apply any property-specific computed value logic
resolve_effective_overflow_values(computed_style);
compute_text_align(computed_style, abstract_element);
resolve_effective_overflow_values(builder);
compute_text_align(builder, abstract_element);
// Let the element adjust computed style
if (!abstract_element.pseudo_element().has_value())
abstract_element.element().adjust_computed_style(computed_style);
abstract_element.element().adjust_computed_style(builder);
// Transition declarations [css-transitions-1]
// Theoretically this should be part of the cascade, but it works with computed values, which we don't have until now.
compute_transitioned_properties(computed_style, abstract_element);
if (auto previous_style = abstract_element.computed_properties()) {
start_needed_transitions(*previous_style, computed_style, abstract_element);
start_needed_transitions(*previous_style, builder, abstract_element);
}
return computed_style;
return CSS::ComputedProperties::create(move(builder));
}
struct SimplifiedSelectorForBucketing {

View file

@ -128,10 +128,11 @@ public:
void set_viewport_rect(Badge<DOM::Document>, CSSPixelRect const& viewport_rect) { m_viewport_rect = viewport_rect; }
void collect_animation_into(DOM::AbstractElement, GC::Ref<Animations::KeyframeEffect> animation, ComputedProperties&) const;
void collect_animation_into(DOM::AbstractElement, GC::Ref<Animations::KeyframeEffect> animation, ComputedProperties::Builder&) const;
[[nodiscard]] NonnullRefPtr<ComputedProperties> compute_properties(DOM::AbstractElement, CascadedProperties&) const;
[[nodiscard]] NonnullRefPtr<ComputedProperties> compute_properties(DOM::AbstractElement, CascadedProperties&, u64 matching_pseudo_element_styles) const;
void compute_property_values(ComputedProperties&, Optional<DOM::AbstractElement>) const;
void compute_property_values(ComputedProperties::Builder&, Optional<DOM::AbstractElement>) const;
void process_animation_definitions(ComputedProperties const& computed_properties, CascadedProperties const&, DOM::AbstractElement& abstract_element) const;
[[nodiscard]] inline bool should_reject_with_ancestor_filter(Selector const&) const;
@ -185,10 +186,11 @@ private:
[[nodiscard]] RefPtr<ComputedProperties> compute_style_impl(DOM::AbstractElement, ComputeStyleMode, Optional<bool&> did_change_custom_properties, StyleScope const&) const;
[[nodiscard]] NonnullRefPtr<CascadedProperties> compute_cascaded_values(DOM::AbstractElement, bool did_match_any_pseudo_element_rules, ComputeStyleMode, MatchingRuleSet const&) const;
void collect_animation_into(DOM::AbstractElement, GC::Ref<Animations::KeyframeEffect> animation, ComputedProperties&, ComputedProperties::Builder*) const;
void compute_custom_properties(ComputedProperties&, DOM::AbstractElement) const;
void start_needed_transitions(ComputedProperties const& old_style, ComputedProperties& new_style, DOM::AbstractElement) const;
void resolve_effective_overflow_values(ComputedProperties&) const;
void transform_box_type_if_needed(ComputedProperties&, DOM::AbstractElement) const;
void start_needed_transitions(ComputedProperties const& old_style, ComputedProperties::Builder& new_style, DOM::AbstractElement) const;
void resolve_effective_overflow_values(ComputedProperties::Builder&) const;
void transform_box_type_if_needed(ComputedProperties::Builder&, DOM::AbstractElement) const;
[[nodiscard]] CSSPixelRect viewport_rect() const { return m_viewport_rect; }

View file

@ -5,6 +5,7 @@
*/
#include <LibWeb/CSS/ComputedProperties.h>
#include <LibWeb/CSS/CustomPropertyData.h>
#include <LibWeb/DOM/AbstractElement.h>
#include <LibWeb/DOM/Document.h>
#include <LibWeb/DOM/Element.h>

View file

@ -7,7 +7,6 @@
#pragma once
#include <LibGC/Cell.h>
#include <LibWeb/CSS/CustomPropertyData.h>
#include <LibWeb/CSS/PseudoElement.h>
#include <LibWeb/Forward.h>

View file

@ -21,6 +21,7 @@
#include <LibURL/Parser.h>
#include <LibUnicode/CharacterTypes.h>
#include <LibUnicode/Locale.h>
#include <LibWeb/Animations/KeyframeEffect.h>
#include <LibWeb/Bindings/Element.h>
#include <LibWeb/Bindings/ExceptionOrUtils.h>
#include <LibWeb/Bindings/MainThreadVM.h>
@ -1071,14 +1072,6 @@ CSS::RequiredInvalidationAfterStyleChange Element::recompute_style(bool& did_cha
auto& style_computer = document().style_computer();
auto new_computed_properties = style_computer.compute_style({ *this }, did_change_custom_properties);
// Tables must not inherit -libweb-* values for text-align.
// FIXME: Find the spec for this.
if (is<HTML::HTMLTableElement>(*this)) {
auto text_align = new_computed_properties->text_align();
if (text_align == CSS::TextAlign::LibwebLeft || text_align == CSS::TextAlign::LibwebCenter || text_align == CSS::TextAlign::LibwebRight)
new_computed_properties->set_property(CSS::PropertyID::TextAlign, CSS::KeywordStyleValue::create(CSS::Keyword::Start));
}
auto old_computed_properties = m_computed_properties;
bool had_list_marker = false;
@ -1140,63 +1133,81 @@ CSS::RequiredInvalidationAfterStyleChange Element::recompute_inherited_style(Sch
auto& counters = document().style_invalidation_counters();
counters.element_inherited_style_recomputations++;
auto computed_properties = this->computed_properties();
VERIFY(computed_properties);
auto had_list_marker = computed_properties->display().is_list_item();
auto old_computed_properties = this->computed_properties();
VERIFY(old_computed_properties);
auto computed_properties_builder = CSS::ComputedProperties::create_builder_with_base_values_from(*old_computed_properties);
auto& new_computed_properties = computed_properties_builder.style();
auto had_list_marker = old_computed_properties->display().is_list_item();
CSS::RequiredInvalidationAfterStyleChange invalidation;
HashMap<size_t, RefPtr<CSS::StyleValue const>> property_values_affected_by_inherited_style;
for (auto const& [property_id, specified_value] : computed_properties->inheritance_dependent_specified_values()) {
RefPtr old_value = computed_properties->property(property_id);
computed_properties->set_property_without_modifying_flags(property_id, specified_value);
for (auto const& [property_id, specified_value] : old_computed_properties->inheritance_dependent_specified_values()) {
RefPtr old_value = old_computed_properties->property(property_id);
computed_properties_builder.set_property_without_modifying_flags(property_id, specified_value);
property_values_affected_by_inherited_style.set(to_underlying(property_id), old_value);
}
bool did_update_animated_properties = false;
for (auto i = to_underlying(CSS::first_longhand_property_id); i <= to_underlying(CSS::last_longhand_property_id); ++i) {
auto property_id = static_cast<CSS::PropertyID>(i);
RefPtr old_value = computed_properties->property(property_id);
RefPtr old_value = old_computed_properties->property(property_id);
if (!computed_properties->is_property_inherited(property_id))
if (!new_computed_properties.is_property_inherited(property_id))
continue;
if (computed_properties->is_animated_property_inherited(property_id) || !computed_properties->animated_property_values().contains(property_id)) {
if (auto new_animated_value = CSS::StyleComputer::get_animated_inherit_value(property_id, { *this }); new_animated_value.has_value())
computed_properties->set_animated_property(property_id, new_animated_value->value, new_animated_value->is_result_of_transition, CSS::ComputedProperties::Inherited::Yes);
else if (computed_properties->animated_property_values().contains(property_id))
computed_properties->remove_animated_property(property_id);
if (new_computed_properties.is_animated_property_inherited(property_id) || !new_computed_properties.has_animated_property(property_id)) {
RefPtr<CSS::StyleValue const> old_animated_value;
if (old_computed_properties->has_animated_property(property_id)) {
auto animated_value = old_computed_properties->animated_property_values().get(property_id);
VERIFY(animated_value.has_value());
old_animated_value = *animated_value.value();
}
if (auto new_animated_value = CSS::StyleComputer::get_animated_inherit_value(property_id, { *this }); new_animated_value.has_value()) {
if (!old_animated_value
|| style_value_changed(*old_animated_value, *new_animated_value->value)
|| old_computed_properties->is_animated_property_result_of_transition(property_id) != (new_animated_value->is_result_of_transition == CSS::AnimatedPropertyResultOfTransition::Yes)
|| !old_computed_properties->is_animated_property_inherited(property_id))
did_update_animated_properties = true;
new_computed_properties.set_animated_property(Badge<DOM::Element> {}, property_id, new_animated_value->value, new_animated_value->is_result_of_transition, CSS::ComputedProperties::Inherited::Yes);
} else if (old_animated_value) {
did_update_animated_properties = true;
new_computed_properties.remove_animated_property(Badge<DOM::Element> {}, property_id);
}
}
RefPtr new_value = CSS::StyleComputer::get_non_animated_inherit_value(property_id, { *this });
computed_properties->set_property(property_id, *new_value, CSS::ComputedProperties::Inherited::Yes);
if (style_value_changed(*old_value, computed_properties->property(property_id)))
computed_properties_builder.set_property(property_id, *new_value, CSS::ComputedProperties::Inherited::Yes);
if (style_value_changed(*old_value, new_computed_properties.property(property_id)))
invalidation.inherited_style_changed = true;
invalidation |= CSS::compute_property_invalidation(property_id, old_value.ptr(), &computed_properties->property(property_id));
invalidation |= CSS::compute_property_invalidation(property_id, old_value.ptr(), &new_computed_properties.property(property_id));
}
if (schedule_animation_update == ScheduleAnimationUpdate::Yes && has_relevant_animations())
document().set_needs_animated_style_update();
if (invalidation.is_none() && property_values_affected_by_inherited_style.is_empty()) {
if (invalidation.is_none() && property_values_affected_by_inherited_style.is_empty() && !did_update_animated_properties) {
counters.element_inherited_style_noop_recomputations++;
return invalidation;
}
AbstractElement abstract_element { *this };
document().style_computer().compute_property_values(*computed_properties, abstract_element);
document().style_computer().compute_property_values(computed_properties_builder, abstract_element);
for (auto const& [property_id_value, old_value] : property_values_affected_by_inherited_style) {
auto property_id = static_cast<CSS::PropertyID>(property_id_value);
auto const& new_value = computed_properties->property(property_id);
auto const& new_value = new_computed_properties.property(property_id);
if (CSS::is_inherited_property(property_id) && style_value_changed(*old_value, new_value))
invalidation.inherited_style_changed = true;
invalidation |= CSS::compute_property_invalidation(property_id, old_value.ptr(), &new_value);
}
m_computed_properties = CSS::ComputedProperties::create(move(computed_properties_builder));
bool did_change_custom_properties = false;
invalidation |= recompute_pseudo_element_styles(did_change_custom_properties, had_list_marker, computed_properties.ptr());
invalidation |= recompute_pseudo_element_styles(did_change_custom_properties, had_list_marker, old_computed_properties.ptr());
if (invalidation.is_none()) {
counters.element_inherited_style_noop_recomputations++;
@ -3665,7 +3676,7 @@ size_t Element::attribute_list_size() const
return m_attributes ? m_attributes->length() : 0;
}
RefPtr<CSS::ComputedProperties> Element::computed_properties(Optional<CSS::PseudoElement> pseudo_element_type)
RefPtr<CSS::ComputedProperties const> Element::computed_properties(Optional<CSS::PseudoElement> pseudo_element_type) const
{
if (pseudo_element_type.has_value()) {
if (auto pseudo_element = get_pseudo_element(*pseudo_element_type); pseudo_element.has_value())
@ -3675,14 +3686,23 @@ RefPtr<CSS::ComputedProperties> Element::computed_properties(Optional<CSS::Pseud
return m_computed_properties;
}
RefPtr<CSS::ComputedProperties const> Element::computed_properties(Optional<CSS::PseudoElement> pseudo_element_type) const
void Element::update_animated_properties(Badge<Web::Animations::KeyframeEffect> const& badge, Optional<CSS::PseudoElement> pseudo_element_type, Web::Animations::KeyframeEffect& effect, Web::Animations::AnimationUpdateContext& context)
{
DOM::AbstractElement abstract_element { *this, pseudo_element_type };
if (pseudo_element_type.has_value()) {
if (auto pseudo_element = get_pseudo_element(*pseudo_element_type); pseudo_element.has_value())
return pseudo_element->computed_properties();
return {};
pseudo_element->update_animated_properties(badge, abstract_element, effect, context);
return;
}
return m_computed_properties;
update_animated_properties_for_abstract_element(badge, abstract_element, effect, context);
}
void Element::update_animated_properties_for_abstract_element(Badge<Web::Animations::KeyframeEffect> const&, DOM::AbstractElement abstract_element, Web::Animations::KeyframeEffect& effect, Web::Animations::AnimationUpdateContext& context)
{
if (!m_computed_properties)
return;
effect.update_computed_properties_for_style(context, abstract_element, *m_computed_properties);
}
void Element::set_computed_properties(Optional<CSS::PseudoElement> pseudo_element_type, RefPtr<CSS::ComputedProperties> style)

View file

@ -6,6 +6,7 @@
#pragma once
#include <AK/Badge.h>
#include <AK/Concepts.h>
#include <AK/Optional.h>
#include <LibGfx/DecodedImageFrame.h>
@ -14,6 +15,7 @@
#include <LibWeb/Bindings/Element.h>
#include <LibWeb/Bindings/Intrinsics.h>
#include <LibWeb/Bindings/ShadowRoot.h>
#include <LibWeb/CSS/ComputedProperties.h>
#include <LibWeb/CSS/Selector.h>
#include <LibWeb/CSS/StyleProperty.h>
#include <LibWeb/DOM/ChildNode.h>
@ -37,6 +39,13 @@
#include <LibWeb/WebIDL/ExceptionOr.h>
#include <LibWeb/WebIDL/Types.h>
namespace Web::Animations {
struct AnimationUpdateContext;
class KeyframeEffect;
}
namespace Web::DOM {
// https://html.spec.whatwg.org/multipage/custom-elements.html#upgrade-reaction
@ -208,9 +217,10 @@ public:
Layout::NodeWithStyle* unsafe_layout_node();
Layout::NodeWithStyle const* unsafe_layout_node() const;
RefPtr<CSS::ComputedProperties> computed_properties(Optional<CSS::PseudoElement> = {});
RefPtr<CSS::ComputedProperties const> computed_properties(Optional<CSS::PseudoElement> = {}) const;
void set_computed_properties(Optional<CSS::PseudoElement>, RefPtr<CSS::ComputedProperties>);
void update_animated_properties(Badge<Web::Animations::KeyframeEffect> const&, Optional<CSS::PseudoElement>, Web::Animations::KeyframeEffect&, Web::Animations::AnimationUpdateContext&);
void update_animated_properties_for_abstract_element(Badge<Web::Animations::KeyframeEffect> const&, DOM::AbstractElement, Web::Animations::KeyframeEffect&, Web::Animations::AnimationUpdateContext&);
Optional<SyntheticPseudoElement&> get_synthetic_pseudo_element(CSS::PseudoElement) const;
Optional<PseudoElement&> get_pseudo_element(CSS::PseudoElement) const;
@ -362,7 +372,7 @@ public:
[[nodiscard]] CSSPixelRect bounding_client_rect_assuming_layout_clean() const;
virtual RefPtr<Layout::Node> create_layout_node(CSS::ComputedProperties const&);
virtual void adjust_computed_style(CSS::ComputedProperties&) { }
virtual void adjust_computed_style(CSS::ComputedProperties::Builder&) { }
virtual void did_receive_focus() { }
virtual void did_lose_focus() { }

View file

@ -4,7 +4,10 @@
* SPDX-License-Identifier: BSD-2-Clause
*/
#include <LibWeb/Animations/KeyframeEffect.h>
#include <LibWeb/CSS/ComputedProperties.h>
#include <LibWeb/CSS/CustomPropertyData.h>
#include <LibWeb/DOM/AbstractElement.h>
#include <LibWeb/DOM/Element.h>
#include <LibWeb/DOM/PseudoElement.h>
#include <LibWeb/Layout/Node.h>
@ -16,6 +19,13 @@ GC_DEFINE_ALLOCATOR(SyntheticPseudoElement);
GC_DEFINE_ALLOCATOR(SyntheticPseudoElementTreeNode);
GC_DEFINE_ALLOCATOR(ElementReferencePseudoElement);
struct SyntheticPseudoElement::CustomPropertyDataStorage {
RefPtr<CSS::CustomPropertyData const> data;
};
SyntheticPseudoElement::SyntheticPseudoElement() = default;
SyntheticPseudoElement::~SyntheticPseudoElement() = default;
void SyntheticPseudoElement::visit_edges(JS::Cell::Visitor& visitor)
{
Base::visit_edges(visitor);
@ -29,6 +39,42 @@ void SyntheticPseudoElement::set_layout_node(Layout::NodeWithStyle* value)
m_layout_node = value;
}
RefPtr<CSS::ComputedProperties const> SyntheticPseudoElement::computed_properties() const
{
return m_computed_properties;
}
void SyntheticPseudoElement::update_animated_properties(Badge<Web::Animations::KeyframeEffect> const&, DOM::AbstractElement abstract_element, Web::Animations::KeyframeEffect& effect, Web::Animations::AnimationUpdateContext& context)
{
if (!m_computed_properties)
return;
effect.update_computed_properties_for_style(context, abstract_element, *m_computed_properties);
}
void SyntheticPseudoElement::set_computed_properties(RefPtr<CSS::ComputedProperties> value)
{
m_computed_properties = value;
}
RefPtr<CSS::CustomPropertyData const> SyntheticPseudoElement::custom_property_data() const
{
if (!m_custom_property_data)
return nullptr;
return m_custom_property_data->data;
}
void SyntheticPseudoElement::set_custom_property_data(RefPtr<CSS::CustomPropertyData const> value)
{
if (!value) {
m_custom_property_data = nullptr;
return;
}
if (!m_custom_property_data)
m_custom_property_data = make<CustomPropertyDataStorage>();
m_custom_property_data->data = move(value);
}
Optional<CSS::CountersSet const&> SyntheticPseudoElement::counters_set() const
{
if (!m_counters_set)
@ -48,6 +94,9 @@ void SyntheticPseudoElement::set_counters_set(OwnPtr<CSS::CountersSet>&& counter
m_counters_set = move(counters_set);
}
SyntheticPseudoElementTreeNode::SyntheticPseudoElementTreeNode() = default;
SyntheticPseudoElementTreeNode::~SyntheticPseudoElementTreeNode() = default;
void SyntheticPseudoElementTreeNode::visit_edges(JS::Cell::Visitor& visitor)
{
Base::visit_edges(visitor);
@ -64,11 +113,16 @@ Layout::NodeWithStyle* ElementReferencePseudoElement::unsafe_layout_node() const
return m_referenced_element->unsafe_layout_node();
}
RefPtr<CSS::ComputedProperties> ElementReferencePseudoElement::computed_properties() const
RefPtr<CSS::ComputedProperties const> ElementReferencePseudoElement::computed_properties() const
{
return m_referenced_element->computed_properties({});
}
void ElementReferencePseudoElement::update_animated_properties(Badge<Web::Animations::KeyframeEffect> const& badge, DOM::AbstractElement abstract_element, Web::Animations::KeyframeEffect& effect, Web::Animations::AnimationUpdateContext& context)
{
m_referenced_element->update_animated_properties_for_abstract_element(badge, abstract_element, effect, context);
}
RefPtr<CSS::CustomPropertyData const> ElementReferencePseudoElement::custom_property_data() const
{
return m_referenced_element->custom_property_data({});

View file

@ -6,17 +6,23 @@
#pragma once
#include <AK/Badge.h>
#include <AK/OwnPtr.h>
#include <AK/WeakPtr.h>
#include <LibGC/CellAllocator.h>
#include <LibJS/Heap/Cell.h>
#include <LibWeb/CSS/ComputedProperties.h>
#include <LibWeb/CSS/CustomPropertyData.h>
#include <LibWeb/Export.h>
#include <LibWeb/Forward.h>
#include <LibWeb/PixelUnits.h>
#include <LibWeb/TreeNode.h>
namespace Web::Animations {
struct AnimationUpdateContext;
class KeyframeEffect;
}
namespace Web::DOM {
class WEB_API PseudoElement : public JS::Cell {
@ -27,7 +33,8 @@ public:
virtual Layout::NodeWithStyle* layout_node() const = 0;
virtual Layout::NodeWithStyle* unsafe_layout_node() const = 0;
virtual RefPtr<CSS::ComputedProperties> computed_properties() const = 0;
virtual RefPtr<CSS::ComputedProperties const> computed_properties() const = 0;
virtual void update_animated_properties(Badge<Web::Animations::KeyframeEffect> const&, DOM::AbstractElement, Web::Animations::KeyframeEffect&, Web::Animations::AnimationUpdateContext&) = 0;
virtual RefPtr<CSS::CustomPropertyData const> custom_property_data() const = 0;
virtual void set_custom_property_data(RefPtr<CSS::CustomPropertyData const> value) = 0;
@ -37,15 +44,20 @@ class WEB_API SyntheticPseudoElement : public PseudoElement {
GC_CELL(SyntheticPseudoElement, PseudoElement);
GC_DECLARE_ALLOCATOR(SyntheticPseudoElement);
public:
SyntheticPseudoElement();
virtual ~SyntheticPseudoElement() override;
Layout::NodeWithStyle* layout_node() const override { return m_layout_node.ptr(); }
Layout::NodeWithStyle* unsafe_layout_node() const override { return m_layout_node.ptr(); }
void set_layout_node(Layout::NodeWithStyle*);
RefPtr<CSS::ComputedProperties> computed_properties() const override { return m_computed_properties; }
void set_computed_properties(RefPtr<CSS::ComputedProperties> value) { m_computed_properties = value; }
RefPtr<CSS::ComputedProperties const> computed_properties() const override;
void update_animated_properties(Badge<Web::Animations::KeyframeEffect> const&, DOM::AbstractElement, Web::Animations::KeyframeEffect&, Web::Animations::AnimationUpdateContext&) override;
void set_computed_properties(RefPtr<CSS::ComputedProperties> value);
RefPtr<CSS::CustomPropertyData const> custom_property_data() const override { return m_custom_property_data; }
void set_custom_property_data(RefPtr<CSS::CustomPropertyData const> value) override { m_custom_property_data = move(value); }
RefPtr<CSS::CustomPropertyData const> custom_property_data() const override;
void set_custom_property_data(RefPtr<CSS::CustomPropertyData const> value) override;
bool has_non_empty_counters_set() const { return m_counters_set; }
Optional<CSS::CountersSet const&> counters_set() const;
@ -58,9 +70,11 @@ class WEB_API SyntheticPseudoElement : public PseudoElement {
virtual void visit_edges(JS::Cell::Visitor&) override;
private:
struct CustomPropertyDataStorage;
WeakPtr<Layout::NodeWithStyle> m_layout_node;
RefPtr<CSS::ComputedProperties> m_computed_properties;
RefPtr<CSS::CustomPropertyData const> m_custom_property_data;
OwnPtr<CustomPropertyDataStorage> m_custom_property_data;
OwnPtr<CSS::CountersSet> m_counters_set;
CSSPixelPoint m_scroll_offset {};
};
@ -72,6 +86,10 @@ class SyntheticPseudoElementTreeNode
GC_CELL(SyntheticPseudoElementTreeNode, SyntheticPseudoElement);
GC_DECLARE_ALLOCATOR(SyntheticPseudoElementTreeNode);
public:
SyntheticPseudoElementTreeNode();
virtual ~SyntheticPseudoElementTreeNode() override;
protected:
virtual void visit_edges(JS::Cell::Visitor& visitor) override;
};
@ -88,7 +106,8 @@ class WEB_API ElementReferencePseudoElement : public PseudoElement {
Layout::NodeWithStyle* layout_node() const override;
Layout::NodeWithStyle* unsafe_layout_node() const override;
RefPtr<CSS::ComputedProperties> computed_properties() const override;
RefPtr<CSS::ComputedProperties const> computed_properties() const override;
void update_animated_properties(Badge<Web::Animations::KeyframeEffect> const&, DOM::AbstractElement, Web::Animations::KeyframeEffect&, Web::Animations::AnimationUpdateContext&) override;
RefPtr<CSS::CustomPropertyData const> custom_property_data() const override;
void set_custom_property_data(RefPtr<CSS::CustomPropertyData const> value) override;

View file

@ -5,6 +5,7 @@
*/
#include "AbstractCanvasMixin.h"
#include <LibWeb/CSS/ComputedProperties.h>
#include <LibWeb/CSS/Parser/Parser.h>
#include <LibWeb/CSS/ValueType.h>
#include <LibWeb/DOM/Document.h>

View file

@ -26,6 +26,7 @@
#include <LibWeb/Bindings/CanvasRenderingContext2D.h>
#include <LibWeb/Bindings/DOMRectReadOnly.h>
#include <LibWeb/Bindings/Intrinsics.h>
#include <LibWeb/CSS/ComputedProperties.h>
#include <LibWeb/CSS/Parser/Parser.h>
#include <LibWeb/CSS/PropertyID.h>
#include <LibWeb/CSS/StyleValues/FilterValueListStyleValue.h>

View file

@ -28,7 +28,7 @@ void HTMLAudioElement::initialize(JS::Realm& realm)
Base::initialize(realm);
}
void HTMLAudioElement::adjust_computed_style(CSS::ComputedProperties& style)
void HTMLAudioElement::adjust_computed_style(CSS::ComputedProperties::Builder& style)
{
Base::adjust_computed_style(style);

View file

@ -17,7 +17,7 @@ class HTMLAudioElement final : public HTMLMediaElement {
public:
virtual ~HTMLAudioElement() override;
virtual void adjust_computed_style(CSS::ComputedProperties& style) override;
virtual void adjust_computed_style(CSS::ComputedProperties::Builder& style) override;
Layout::AudioBox* layout_node();
Layout::AudioBox const* layout_node() const;

View file

@ -58,7 +58,7 @@ void HTMLBRElement::apply_presentational_hints(Vector<CSS::StyleProperty>& prope
});
}
void HTMLBRElement::adjust_computed_style(CSS::ComputedProperties& style)
void HTMLBRElement::adjust_computed_style(CSS::ComputedProperties::Builder& style)
{
// https://drafts.csswg.org/css-display-3/#unbox
if (style.display().is_contents())

View file

@ -20,7 +20,7 @@ public:
virtual RefPtr<Layout::Node> create_layout_node(CSS::ComputedProperties const&) override;
virtual bool is_presentational_hint(FlyString const&) const override;
virtual void apply_presentational_hints(Vector<CSS::StyleProperty>&) const override;
virtual void adjust_computed_style(CSS::ComputedProperties&) override;
virtual void adjust_computed_style(CSS::ComputedProperties::Builder&) override;
private:
virtual bool is_html_br_element() const override { return true; }

View file

@ -31,7 +31,7 @@ void HTMLButtonElement::initialize(JS::Realm& realm)
Base::initialize(realm);
}
void HTMLButtonElement::adjust_computed_style(CSS::ComputedProperties& style)
void HTMLButtonElement::adjust_computed_style(CSS::ComputedProperties::Builder& style)
{
// https://html.spec.whatwg.org/multipage/rendering.html#button-layout
// If the computed value of 'display' is 'inline-grid', 'grid', 'inline-flex', 'flex', 'none', or 'contents', then behave as the computed value.

View file

@ -28,7 +28,7 @@ public:
virtual ~HTMLButtonElement() override;
virtual void initialize(JS::Realm&) override;
virtual void adjust_computed_style(CSS::ComputedProperties&) override;
virtual void adjust_computed_style(CSS::ComputedProperties::Builder&) override;
enum class TypeAttributeState {
#define __ENUMERATE_HTML_BUTTON_TYPE_ATTRIBUTE(_, state) state,

View file

@ -248,7 +248,7 @@ RefPtr<Layout::Node> HTMLCanvasElement::create_layout_node(CSS::ComputedProperti
return make_ref_counted<Layout::CanvasBox>(document(), *this, style);
}
void HTMLCanvasElement::adjust_computed_style(CSS::ComputedProperties& style)
void HTMLCanvasElement::adjust_computed_style(CSS::ComputedProperties::Builder& style)
{
// https://drafts.csswg.org/css-display-3/#unbox
if (style.display().is_contents())

View file

@ -78,7 +78,7 @@ private:
virtual void apply_presentational_hints(Vector<CSS::StyleProperty>&) const override;
virtual RefPtr<Layout::Node> create_layout_node(CSS::ComputedProperties const&) override;
virtual void adjust_computed_style(CSS::ComputedProperties&) override;
virtual void adjust_computed_style(CSS::ComputedProperties::Builder&) override;
template<typename ContextType>
JS::ThrowCompletionOr<HasOrCreatedContext> create_webgl_context(JS::Value options);

View file

@ -1158,7 +1158,7 @@ void HTMLElement::set_popover(Optional<String> value)
remove_attribute(HTML::AttributeNames::popover);
}
void HTMLElement::adjust_computed_style(CSS::ComputedProperties& style)
void HTMLElement::adjust_computed_style(CSS::ComputedProperties::Builder& style)
{
// https://drafts.csswg.org/css-display-3/#unbox
if (local_name() == HTML::TagNames::wbr) {

View file

@ -206,14 +206,14 @@ protected:
[[nodiscard]] Utf16String get_the_text_steps();
virtual void adjust_computed_style(CSS::ComputedProperties::Builder&) override;
private:
virtual bool is_html_element() const final { return true; }
// ^FormAssociatedElement
virtual HTMLElement& form_associated_element_to_html_element() override { return *this; }
virtual void adjust_computed_style(CSS::ComputedProperties&) override;
// ^HTML::GlobalEventHandlers
virtual GC::Ptr<DOM::EventTarget> global_event_handlers_to_event_target(FlyString const&) override { return *this; }
virtual void did_receive_focus() override;

View file

@ -79,7 +79,7 @@ void HTMLEmbedElement::apply_presentational_hints(Vector<CSS::StyleProperty>& pr
});
}
void HTMLEmbedElement::adjust_computed_style(CSS::ComputedProperties& style)
void HTMLEmbedElement::adjust_computed_style(CSS::ComputedProperties::Builder& style)
{
// https://drafts.csswg.org/css-display-3/#unbox
if (style.display().is_contents())

View file

@ -24,7 +24,7 @@ private:
virtual void initialize(JS::Realm&) override;
virtual bool is_presentational_hint(FlyString const&) const override;
virtual void apply_presentational_hints(Vector<CSS::StyleProperty>&) const override;
virtual void adjust_computed_style(CSS::ComputedProperties&) override;
virtual void adjust_computed_style(CSS::ComputedProperties::Builder&) override;
};
}

View file

@ -82,7 +82,7 @@ i32 HTMLFrameElement::default_tab_index_value() const
return 0;
}
void HTMLFrameElement::adjust_computed_style(CSS::ComputedProperties& style)
void HTMLFrameElement::adjust_computed_style(CSS::ComputedProperties::Builder& style)
{
// https://drafts.csswg.org/css-display-3/#unbox
if (style.display().is_contents())

View file

@ -29,7 +29,7 @@ private:
virtual void removed_from(IsSubtreeRoot, Node* old_ancestor, Node& old_root) override;
virtual void attribute_changed(FlyString const& name, Optional<String> const& old_value, Optional<String> const& value, Optional<FlyString> const& namespace_) override;
virtual i32 default_tab_index_value() const override;
virtual void adjust_computed_style(CSS::ComputedProperties&) override;
virtual void adjust_computed_style(CSS::ComputedProperties::Builder&) override;
void process_the_frame_attributes(InitialInsertion = InitialInsertion::No);
};

View file

@ -23,7 +23,7 @@ HTMLFrameSetElement::HTMLFrameSetElement(DOM::Document& document, DOM::Qualified
HTMLFrameSetElement::~HTMLFrameSetElement() = default;
void HTMLFrameSetElement::adjust_computed_style(CSS::ComputedProperties& style)
void HTMLFrameSetElement::adjust_computed_style(CSS::ComputedProperties::Builder& style)
{
// https://drafts.csswg.org/css-display-3/#unbox
if (style.display().is_contents())

View file

@ -26,7 +26,7 @@ private:
virtual bool is_html_frameset_element() const override { return true; }
virtual void adjust_computed_style(CSS::ComputedProperties&) override;
virtual void adjust_computed_style(CSS::ComputedProperties::Builder&) override;
virtual void initialize(JS::Realm&) override;
virtual void attribute_changed(FlyString const& name, Optional<String> const& old_value, Optional<String> const& value, Optional<FlyString> const& namespace_) override;

View file

@ -46,7 +46,7 @@ RefPtr<Layout::Node> HTMLIFrameElement::create_layout_node(CSS::ComputedProperti
return make_ref_counted<Layout::NavigableContainerViewport>(document(), *this, style);
}
void HTMLIFrameElement::adjust_computed_style(CSS::ComputedProperties& style)
void HTMLIFrameElement::adjust_computed_style(CSS::ComputedProperties::Builder& style)
{
// https://drafts.csswg.org/css-display-3/#unbox
if (style.display().is_contents())

View file

@ -24,7 +24,7 @@ public:
virtual ~HTMLIFrameElement() override;
virtual RefPtr<Layout::Node> create_layout_node(CSS::ComputedProperties const&) override;
virtual void adjust_computed_style(CSS::ComputedProperties&) override;
virtual void adjust_computed_style(CSS::ComputedProperties::Builder&) override;
// ^EventTarget
virtual bool is_focusable() const override

View file

@ -290,7 +290,7 @@ RefPtr<Layout::Node> HTMLImageElement::create_layout_node(CSS::ComputedPropertie
return make_ref_counted<Layout::ImageBox>(document(), *this, style, *this);
}
void HTMLImageElement::adjust_computed_style(CSS::ComputedProperties& style)
void HTMLImageElement::adjust_computed_style(CSS::ComputedProperties::Builder& style)
{
// https://drafts.csswg.org/css-display-3/#unbox
if (style.display().is_contents())

View file

@ -128,7 +128,7 @@ private:
virtual bool supports_dimension_attributes() const override { return true; }
virtual RefPtr<Layout::Node> create_layout_node(CSS::ComputedProperties const&) override;
virtual void adjust_computed_style(CSS::ComputedProperties&) override;
virtual void adjust_computed_style(CSS::ComputedProperties::Builder&) override;
virtual void did_set_viewport_rect(CSSPixelRect const&) override;

View file

@ -178,7 +178,7 @@ RefPtr<Layout::Node> HTMLInputElement::create_layout_node(CSS::ComputedPropertie
}
}
void HTMLInputElement::adjust_computed_style(CSS::ComputedProperties& style)
void HTMLInputElement::adjust_computed_style(CSS::ComputedProperties::Builder& style)
{
if (type_state() == TypeAttributeState::Hidden || type_state() == TypeAttributeState::SubmitButton || type_state() == TypeAttributeState::Button || type_state() == TypeAttributeState::ResetButton || type_state() == TypeAttributeState::ImageButton || type_state() == TypeAttributeState::Checkbox || type_state() == TypeAttributeState::RadioButton)
return;

View file

@ -65,7 +65,7 @@ public:
virtual ~HTMLInputElement() override;
virtual RefPtr<Layout::Node> create_layout_node(CSS::ComputedProperties const&) override;
virtual void adjust_computed_style(CSS::ComputedProperties&) override;
virtual void adjust_computed_style(CSS::ComputedProperties::Builder&) override;
virtual void set_being_activated(bool) override;
enum class TypeAttributeState {

View file

@ -137,7 +137,7 @@ void HTMLMediaElement::finalize()
document().page().unregister_media_element({}, unique_id());
}
void HTMLMediaElement::adjust_computed_style(CSS::ComputedProperties& style)
void HTMLMediaElement::adjust_computed_style(CSS::ComputedProperties::Builder& style)
{
// https://drafts.csswg.org/css-display-3/#unbox
if (style.display().is_contents())

View file

@ -50,7 +50,7 @@ public:
return meets_focusable_area_rendering_requirements();
}
virtual void adjust_computed_style(CSS::ComputedProperties& style) override;
virtual void adjust_computed_style(CSS::ComputedProperties::Builder& style) override;
// NOTE: The function is wrapped in a GC::HeapFunction immediately.
void queue_a_media_element_task(Function<void(HTMLMediaElement&)>);

View file

@ -170,7 +170,7 @@ void HTMLMeterElement::inserted()
create_shadow_tree_if_needed();
}
void HTMLMeterElement::adjust_computed_style(CSS::ComputedProperties& style)
void HTMLMeterElement::adjust_computed_style(CSS::ComputedProperties::Builder& style)
{
// https://drafts.csswg.org/css-display-3/#unbox
if (style.display().is_contents())

View file

@ -36,7 +36,7 @@ public:
// ^HTMLElement
virtual void inserted() override;
virtual void adjust_computed_style(CSS::ComputedProperties&) override;
virtual void adjust_computed_style(CSS::ComputedProperties::Builder&) override;
// https://html.spec.whatwg.org/multipage/forms.html#category-label
virtual bool is_labelable() const override { return true; }

View file

@ -221,7 +221,7 @@ RefPtr<Layout::Node> HTMLObjectElement::create_layout_node(CSS::ComputedProperti
return nullptr;
}
void HTMLObjectElement::adjust_computed_style(CSS::ComputedProperties& style)
void HTMLObjectElement::adjust_computed_style(CSS::ComputedProperties::Builder& style)
{
// https://drafts.csswg.org/css-display-3/#unbox
if (style.display().is_contents())

View file

@ -64,7 +64,7 @@ private:
virtual void apply_presentational_hints(Vector<CSS::StyleProperty>&) const override;
virtual RefPtr<Layout::Node> create_layout_node(CSS::ComputedProperties const&) override;
virtual void adjust_computed_style(CSS::ComputedProperties&) override;
virtual void adjust_computed_style(CSS::ComputedProperties::Builder&) override;
bool has_ancestor_media_element_or_object_element_not_showing_fallback_content() const;

View file

@ -95,7 +95,7 @@ void HTMLProgressElement::inserted()
create_shadow_tree_if_needed();
}
void HTMLProgressElement::adjust_computed_style(CSS::ComputedProperties& style)
void HTMLProgressElement::adjust_computed_style(CSS::ComputedProperties::Builder& style)
{
// https://drafts.csswg.org/css-display-3/#unbox
if (style.display().is_contents())

View file

@ -30,7 +30,7 @@ public:
// ^HTMLElement
virtual void inserted() override;
virtual void adjust_computed_style(CSS::ComputedProperties&) override;
virtual void adjust_computed_style(CSS::ComputedProperties::Builder&) override;
// https://html.spec.whatwg.org/multipage/forms.html#category-label
virtual bool is_labelable() const override { return true; }

View file

@ -79,7 +79,7 @@ void HTMLSelectElement::visit_edges(Cell::Visitor& visitor)
}
}
void HTMLSelectElement::adjust_computed_style(CSS::ComputedProperties& style)
void HTMLSelectElement::adjust_computed_style(CSS::ComputedProperties::Builder& style)
{
// https://drafts.csswg.org/css-display-3/#unbox
if (style.display().is_contents())

View file

@ -30,7 +30,7 @@ public:
virtual bool is_html_select_element() const final { return true; }
virtual void adjust_computed_style(CSS::ComputedProperties&) override;
virtual void adjust_computed_style(CSS::ComputedProperties::Builder&) override;
WebIDL::UnsignedLong size() const;
void set_size(WebIDL::UnsignedLong);

View file

@ -49,6 +49,17 @@ void HTMLTableElement::visit_edges(Cell::Visitor& visitor)
visitor.visit(m_t_bodies);
}
void HTMLTableElement::adjust_computed_style(CSS::ComputedProperties::Builder& style)
{
Base::adjust_computed_style(style);
// Tables must not inherit -libweb-* values for text-align.
// FIXME: Find the spec for this.
auto text_align = style.style().text_align();
if (text_align == CSS::TextAlign::LibwebLeft || text_align == CSS::TextAlign::LibwebCenter || text_align == CSS::TextAlign::LibwebRight)
style.set_property(CSS::PropertyID::TextAlign, CSS::KeywordStyleValue::create(CSS::Keyword::Start));
}
static unsigned parse_border(StringView value)
{
return value.to_number<unsigned>().value_or(0);

View file

@ -61,6 +61,7 @@ private:
virtual bool is_presentational_hint(FlyString const&) const override;
virtual void apply_presentational_hints(Vector<CSS::StyleProperty>&) const override;
virtual void attribute_changed(FlyString const& name, Optional<String> const& old_value, Optional<String> const& value, Optional<FlyString> const& namespace_) override;
virtual void adjust_computed_style(CSS::ComputedProperties::Builder&) override;
GC::Ptr<DOM::HTMLCollection> mutable m_rows;
GC::Ptr<DOM::HTMLCollection> mutable m_t_bodies;

View file

@ -40,7 +40,7 @@ HTMLTextAreaElement::HTMLTextAreaElement(DOM::Document& document, DOM::Qualified
HTMLTextAreaElement::~HTMLTextAreaElement() = default;
void HTMLTextAreaElement::adjust_computed_style(CSS::ComputedProperties& style)
void HTMLTextAreaElement::adjust_computed_style(CSS::ComputedProperties::Builder& style)
{
// https://drafts.csswg.org/css-display-3/#unbox
if (style.display().is_contents())

View file

@ -32,7 +32,7 @@ class WEB_API HTMLTextAreaElement final
public:
virtual ~HTMLTextAreaElement() override;
virtual void adjust_computed_style(CSS::ComputedProperties&) override;
virtual void adjust_computed_style(CSS::ComputedProperties::Builder&) override;
String const& type() const
{

View file

@ -514,7 +514,6 @@ RefPtr<NodeWithStyle> TreeBuilder::create_pseudo_element_if_needed(DOM::Element&
element,
*pseudo_element_style);
list_box->set_marker(list_item_marker);
element.set_computed_properties(CSS::PseudoElement::Marker, pseudo_element_style);
element.set_synthetic_pseudo_element_node({}, CSS::PseudoElement::Marker, list_item_marker);
list_box->prepend_child(*list_item_marker);
return list_item_marker;
@ -877,7 +876,7 @@ void TreeBuilder::update_layout_tree(DOM::Node& dom_node, TreeBuilder::Context&
}
auto& style_computer = document.style_computer();
RefPtr<CSS::ComputedProperties> style;
RefPtr<CSS::ComputedProperties const> style;
CSS::Display display;
if (!should_create_layout_node) {

View file

@ -294,7 +294,7 @@ void SVGElement::remove_from_use_element_that_reference_this()
}
}
void SVGElement::adjust_computed_style(CSS::ComputedProperties& computed_properties)
void SVGElement::adjust_computed_style(CSS::ComputedProperties::Builder& computed_properties)
{
Base::adjust_computed_style(computed_properties);

View file

@ -48,7 +48,7 @@ protected:
virtual void children_changed(ChildrenChangedMetadata const&) override;
virtual void inserted() override;
virtual void removed_from(IsSubtreeRoot, Node* old_ancestor, Node& old_root) override;
MUST_UPCALL virtual void adjust_computed_style(CSS::ComputedProperties&) override;
MUST_UPCALL virtual void adjust_computed_style(CSS::ComputedProperties::Builder&) override;
void update_use_elements_that_reference_this();
void remove_from_use_element_that_reference_this();

View file

@ -35,7 +35,7 @@ void SVGSymbolElement::visit_edges(Cell::Visitor& visitor)
SVGFitToViewBox::visit_edges(visitor);
}
void SVGSymbolElement::adjust_computed_style(CSS::ComputedProperties& computed_properties)
void SVGSymbolElement::adjust_computed_style(CSS::ComputedProperties::Builder& computed_properties)
{
Base::adjust_computed_style(computed_properties);

View file

@ -19,7 +19,7 @@ class SVGSymbolElement final : public SVGGraphicsElement
public:
virtual ~SVGSymbolElement() override = default;
virtual void adjust_computed_style(CSS::ComputedProperties&) override;
virtual void adjust_computed_style(CSS::ComputedProperties::Builder&) override;
private:
virtual bool is_svg_symbol_element() const final { return true; }

View file

@ -9,6 +9,7 @@
#include <LibWeb/CSS/CSSKeyframesRule.h>
#include <LibWeb/CSS/CSSStyleRule.h>
#include <LibWeb/CSS/CSSStyleSheet.h>
#include <LibWeb/CSS/ComputedProperties.h>
#include <LibWeb/CSS/PropertyID.h>
#include <LibWeb/DOM/Document.h>
#include <LibWeb/HTML/EventLoop/EventLoop.h>

View file

@ -3,11 +3,13 @@ initial inherited-font animated width: 45px
initial root-font animated width: 45px
initial animated font size: 45px
initial animated inherited-font width: 67.5px
initial post-style animated width: 45px
targeted pending animated outline width: 75px
resized viewport animated width: 75px
resized inherited-font animated width: 75px
resized root-font animated width: 75px
resized animated font size: 75px
resized animated inherited-font width: 112.5px
resized post-style animated width: 75px
full invalidations: 0
style recomputations bounded: true

View file

@ -32,6 +32,7 @@
<div id="root-font-target"></div>
<div id="animated-font-source"><div id="animated-inherited-font-target"></div></div>
<div id="pending-outline-animation-target"></div>
<div id="post-style-animation-target"></div>
<script>
function pauseHalfway(animation) {
animation.pause();
@ -70,6 +71,7 @@
const animatedFontSource = child.document.getElementById("animated-font-source");
const animatedInheritedFontTarget = child.document.getElementById("animated-inherited-font-target");
const pendingOutlineAnimationTarget = child.document.getElementById("pending-outline-animation-target");
const postStyleAnimationTarget = child.document.getElementById("post-style-animation-target");
child.internals.updateStyle();
println(`initial viewport animated width: ${child.getComputedStyle(viewportTarget).width}`);
@ -78,6 +80,15 @@
println(`initial animated font size: ${child.getComputedStyle(animatedFontSource).fontSize}`);
println(`initial animated inherited-font width: ${child.getComputedStyle(animatedInheritedFontTarget).width}`);
const postStyleAnimation = postStyleAnimationTarget.animate([{ width: "10vw" }, { width: "20vw" }], {
duration: 1000,
fill: "both",
});
postStyleAnimation.pause();
postStyleAnimation.currentTime = 500;
child.internals.updateStyle();
println(`initial post-style animated width: ${child.getComputedStyle(postStyleAnimationTarget).width}`);
const pendingOutlineAnimation = pendingOutlineAnimationTarget.animate([{ outlineWidth: "10vw" }, { outlineWidth: "20vw" }], {
duration: 1000,
fill: "both",
@ -101,6 +112,7 @@
println(`resized root-font animated width: ${child.getComputedStyle(rootFontTarget).width}`);
println(`resized animated font size: ${child.getComputedStyle(animatedFontSource).fontSize}`);
println(`resized animated inherited-font width: ${child.getComputedStyle(animatedInheritedFontTarget).width}`);
println(`resized post-style animated width: ${child.getComputedStyle(postStyleAnimationTarget).width}`);
println(`full invalidations: ${counters.fullStyleInvalidations}`);
println(`style recomputations bounded: ${counters.elementStyleRecomputations < elementCount}`);