LibJS+LibWeb: Add missing GC marking visits
This adds visit_edges(Cell::Visitor&) methods to various helper structs that contain GC pointers, and makes sure they are called from owning GC-heap-allocated objects as needed. These were found by our Clang plugin after expanding its capabilities. The added rules will be enforced by CI going forward.
This commit is contained in:
parent
2677338f43
commit
a9cc425cde
41 changed files with 322 additions and 38 deletions
|
|
@ -95,6 +95,7 @@ void Executable::visit_edges(Visitor& visitor)
|
|||
visitor.visit(constants);
|
||||
for (auto& cache : template_object_caches)
|
||||
visitor.visit(cache.cached_template_object);
|
||||
property_key_table->visit_edges(visitor);
|
||||
}
|
||||
|
||||
Optional<Executable::ExceptionHandlers const&> Executable::exception_handlers_for_offset(size_t offset) const
|
||||
|
|
|
|||
|
|
@ -1396,6 +1396,10 @@ private:
|
|||
{
|
||||
Base::visit_edges(visitor);
|
||||
visitor.visit(m_object);
|
||||
for (auto& key : m_properties)
|
||||
key.visit_edges(visitor);
|
||||
if (!m_iterator.is_end())
|
||||
m_iterator->visit_edges(visitor);
|
||||
}
|
||||
|
||||
GC::Ref<Object> m_object;
|
||||
|
|
|
|||
|
|
@ -32,6 +32,12 @@ public:
|
|||
|
||||
ReadonlySpan<PropertyKey const> property_keys() const { return m_property_keys; }
|
||||
|
||||
void visit_edges(GC::Cell::Visitor& visitor)
|
||||
{
|
||||
for (auto& key : m_property_keys)
|
||||
key.visit_edges(visitor);
|
||||
}
|
||||
|
||||
private:
|
||||
Vector<PropertyKey> m_property_keys;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -65,6 +65,7 @@ set(SOURCES
|
|||
Runtime/BooleanObject.cpp
|
||||
Runtime/BooleanPrototype.cpp
|
||||
Runtime/BoundFunction.cpp
|
||||
Runtime/ClassFieldDefinition.cpp
|
||||
Runtime/Completion.cpp
|
||||
Runtime/CompletionCell.cpp
|
||||
Runtime/ConsoleObjectPrototype.cpp
|
||||
|
|
|
|||
23
Libraries/LibJS/Runtime/ClassFieldDefinition.cpp
Normal file
23
Libraries/LibJS/Runtime/ClassFieldDefinition.cpp
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
/*
|
||||
* Copyright (c) 2026, Andreas Kling <andreas@ladybird.org>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#include <LibJS/Runtime/ClassFieldDefinition.h>
|
||||
#include <LibJS/Runtime/ECMAScriptFunctionObject.h>
|
||||
|
||||
namespace JS {
|
||||
|
||||
void ClassFieldDefinition::visit_edges(Cell::Visitor& visitor)
|
||||
{
|
||||
name.visit(
|
||||
[&](PropertyKey const& key) { key.visit_edges(visitor); },
|
||||
[&](PrivateName const&) {});
|
||||
initializer.visit(
|
||||
[&](GC::Ref<ECMAScriptFunctionObject>& function) { visitor.visit(function); },
|
||||
[&](Value& value) { visitor.visit(value); },
|
||||
[&](Empty) {});
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -19,6 +19,8 @@ using ClassElementName = Variant<PropertyKey, PrivateName>;
|
|||
struct ClassFieldDefinition {
|
||||
ClassElementName name; // [[Name]]
|
||||
Variant<GC::Ref<ECMAScriptFunctionObject>, Value, Empty> initializer; // [[Initializer]]
|
||||
|
||||
void visit_edges(Cell::Visitor& visitor);
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -108,6 +108,11 @@ public:
|
|||
return { m_type, release_value() };
|
||||
}
|
||||
|
||||
void visit_edges(Cell::Visitor& visitor)
|
||||
{
|
||||
visitor.visit(m_value);
|
||||
}
|
||||
|
||||
private:
|
||||
class EmptyTag {
|
||||
};
|
||||
|
|
@ -223,6 +228,11 @@ namespace JS {
|
|||
|
||||
struct ErrorValue {
|
||||
Value error;
|
||||
|
||||
void visit_edges(Cell::Visitor& visitor)
|
||||
{
|
||||
visitor.visit(error);
|
||||
}
|
||||
};
|
||||
|
||||
template<typename ValueType>
|
||||
|
|
@ -295,6 +305,21 @@ public:
|
|||
return Completion { Completion::Type::Throw, error.error };
|
||||
}
|
||||
|
||||
void visit_edges(Cell::Visitor& visitor)
|
||||
{
|
||||
m_value_or_error.visit(
|
||||
[&](ValueType& value) {
|
||||
if constexpr (IsSame<ValueType, Value>) {
|
||||
visitor.visit(value);
|
||||
} else if constexpr (requires { value.visit_edges(visitor); }) {
|
||||
value.visit_edges(visitor);
|
||||
}
|
||||
},
|
||||
[&](ErrorValue& error) {
|
||||
error.visit_edges(visitor);
|
||||
});
|
||||
}
|
||||
|
||||
private:
|
||||
Variant<ValueType, ErrorValue> m_value_or_error;
|
||||
};
|
||||
|
|
@ -333,6 +358,11 @@ public:
|
|||
Completion error() const { return Completion { Completion::Type::Throw, m_value }; }
|
||||
Completion release_error() { return error(); }
|
||||
|
||||
void visit_edges(Cell::Visitor& visitor)
|
||||
{
|
||||
visitor.visit(m_value);
|
||||
}
|
||||
|
||||
private:
|
||||
Value m_value;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@
|
|||
|
||||
#include <LibGC/Heap.h>
|
||||
#include <LibJS/Bytecode/Executable.h>
|
||||
#include <LibJS/Runtime/DeclarativeEnvironment.h>
|
||||
#include <LibJS/Runtime/ExecutionContext.h>
|
||||
#include <LibJS/Runtime/FunctionObject.h>
|
||||
|
||||
|
|
@ -139,6 +140,9 @@ void ExecutionContext::visit_edges(Cell::Visitor& visitor)
|
|||
visitor.visit(this_value);
|
||||
visitor.visit(executable);
|
||||
visitor.visit(registers_and_constants_and_locals_and_arguments_span());
|
||||
visitor.visit(global_object);
|
||||
visitor.visit(global_declarative_environment);
|
||||
visitor.visit(arguments);
|
||||
script_or_module.visit(
|
||||
[](Empty) {},
|
||||
[&](auto& script_or_module) {
|
||||
|
|
|
|||
|
|
@ -18,6 +18,11 @@ struct ValueAndAttributes {
|
|||
PropertyAttributes attributes { default_attributes };
|
||||
|
||||
Optional<u32> property_offset {};
|
||||
|
||||
void visit_edges(Cell::Visitor& visitor)
|
||||
{
|
||||
visitor.visit(value);
|
||||
}
|
||||
};
|
||||
|
||||
class IndexedProperties;
|
||||
|
|
@ -45,6 +50,8 @@ public:
|
|||
size_t array_like_size() const { return m_array_size; }
|
||||
virtual bool set_array_like_size(size_t new_size) = 0;
|
||||
|
||||
virtual void visit_edges(Cell::Visitor&) = 0;
|
||||
|
||||
bool is_simple_storage() const { return m_is_simple_storage; }
|
||||
|
||||
protected:
|
||||
|
|
@ -79,6 +86,12 @@ public:
|
|||
virtual size_t size() const override { return m_packed_elements.size(); }
|
||||
virtual bool set_array_like_size(size_t new_size) override;
|
||||
|
||||
virtual void visit_edges(Cell::Visitor& visitor) override
|
||||
{
|
||||
for (auto& value : m_packed_elements)
|
||||
visitor.visit(value);
|
||||
}
|
||||
|
||||
Vector<Value> const& elements() const { return m_packed_elements; }
|
||||
|
||||
[[nodiscard]] bool inline_has_index(u32 index) const
|
||||
|
|
@ -123,6 +136,12 @@ public:
|
|||
virtual size_t size() const override { return m_sparse_elements.size(); }
|
||||
virtual bool set_array_like_size(size_t new_size) override;
|
||||
|
||||
virtual void visit_edges(Cell::Visitor& visitor) override
|
||||
{
|
||||
for (auto& element : m_sparse_elements)
|
||||
element.value.visit_edges(visitor);
|
||||
}
|
||||
|
||||
HashMap<u32, ValueAndAttributes> const& sparse_elements() const { return m_sparse_elements; }
|
||||
|
||||
private:
|
||||
|
|
@ -196,6 +215,12 @@ public:
|
|||
}
|
||||
}
|
||||
|
||||
void visit_edges(Cell::Visitor& visitor)
|
||||
{
|
||||
if (m_storage)
|
||||
m_storage->visit_edges(visitor);
|
||||
}
|
||||
|
||||
private:
|
||||
void switch_to_generic_storage();
|
||||
void ensure_storage();
|
||||
|
|
|
|||
|
|
@ -66,6 +66,11 @@ public:
|
|||
bool operator==(IteratorImpl const& other) const { return m_index == other.m_index && &m_map == &other.m_map; }
|
||||
bool operator==(EndIterator const&) const { return is_end(); }
|
||||
|
||||
void visit_edges(Cell::Visitor& visitor)
|
||||
{
|
||||
visitor.visit(m_map);
|
||||
}
|
||||
|
||||
private:
|
||||
friend class Map;
|
||||
IteratorImpl(Map const& map)
|
||||
|
|
|
|||
|
|
@ -30,6 +30,7 @@ void MapIterator::visit_edges(Cell::Visitor& visitor)
|
|||
{
|
||||
Base::visit_edges(visitor);
|
||||
visitor.visit(m_map);
|
||||
m_iterator.visit_edges(visitor);
|
||||
}
|
||||
|
||||
BuiltinIterator* MapIterator::as_builtin_iterator_if_next_is_not_redefined(Value next_method)
|
||||
|
|
|
|||
|
|
@ -1532,13 +1532,11 @@ void Object::visit_edges(Cell::Visitor& visitor)
|
|||
visitor.visit(m_shape);
|
||||
visitor.visit(m_storage);
|
||||
|
||||
m_indexed_properties.for_each_value([&visitor](auto& value) {
|
||||
visitor.visit(value);
|
||||
});
|
||||
m_indexed_properties.visit_edges(visitor);
|
||||
|
||||
if (m_private_elements) {
|
||||
for (auto& private_element : *m_private_elements)
|
||||
visitor.visit(private_element.value);
|
||||
private_element.visit_edges(visitor);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -37,6 +37,11 @@ struct PrivateElement {
|
|||
PrivateName key;
|
||||
Kind kind { Kind::Field };
|
||||
Value value;
|
||||
|
||||
void visit_edges(Cell::Visitor& visitor)
|
||||
{
|
||||
visitor.visit(value);
|
||||
}
|
||||
};
|
||||
|
||||
// Non-standard: This is information optionally returned by object property access functions.
|
||||
|
|
|
|||
|
|
@ -123,6 +123,20 @@ public:
|
|||
|
||||
Optional<EnvironmentCoordinate> environment_coordinate() const { return m_environment_coordinate; }
|
||||
|
||||
void visit_edges(Cell::Visitor& visitor)
|
||||
{
|
||||
if (m_base_type == BaseType::Value) {
|
||||
visitor.visit(m_base_value);
|
||||
} else if (m_base_type == BaseType::Environment) {
|
||||
visitor.visit(m_base_environment);
|
||||
}
|
||||
m_name.visit(
|
||||
[&](PropertyKey const& key) { key.visit_edges(visitor); },
|
||||
[&](PrivateName const&) { /* no GC pointers */ });
|
||||
if (m_this_value.has_value())
|
||||
visitor.visit(*m_this_value);
|
||||
}
|
||||
|
||||
private:
|
||||
Completion throw_reference_error(VM&) const;
|
||||
|
||||
|
|
|
|||
|
|
@ -29,6 +29,7 @@ void SetIterator::visit_edges(Cell::Visitor& visitor)
|
|||
{
|
||||
Base::visit_edges(visitor);
|
||||
visitor.visit(m_set);
|
||||
m_iterator.visit_edges(visitor);
|
||||
}
|
||||
|
||||
BuiltinIterator* SetIterator::as_builtin_iterator_if_next_is_not_redefined(Value next_method)
|
||||
|
|
|
|||
|
|
@ -210,6 +210,11 @@ void Shape::visit_edges(Cell::Visitor& visitor)
|
|||
}
|
||||
|
||||
visitor.visit(m_prototype_chain_validity);
|
||||
|
||||
if (m_property_table) {
|
||||
for (auto& it : *m_property_table)
|
||||
it.key.visit_edges(visitor);
|
||||
}
|
||||
}
|
||||
|
||||
Optional<PropertyMetadata> Shape::lookup(PropertyKey const& property_key) const
|
||||
|
|
|
|||
|
|
@ -34,6 +34,11 @@ struct TransitionKey {
|
|||
{
|
||||
return property_key == other.property_key && attributes == other.attributes;
|
||||
}
|
||||
|
||||
void visit_edges(Cell::Visitor& visitor)
|
||||
{
|
||||
property_key.visit_edges(visitor);
|
||||
}
|
||||
};
|
||||
|
||||
class PrototypeChainValidity final : public Cell {
|
||||
|
|
@ -100,11 +105,6 @@ public:
|
|||
OrderedHashMap<PropertyKey, PropertyMetadata> const& property_table() const;
|
||||
u32 property_count() const { return m_property_count; }
|
||||
|
||||
struct Property {
|
||||
PropertyKey key;
|
||||
PropertyMetadata value;
|
||||
};
|
||||
|
||||
void set_prototype_without_transition(Object* new_prototype);
|
||||
|
||||
private:
|
||||
|
|
|
|||
|
|
@ -292,6 +292,7 @@ void SharedFunctionInstanceData::visit_edges(Visitor& visitor)
|
|||
{
|
||||
Base::visit_edges(visitor);
|
||||
visitor.visit(m_executable);
|
||||
m_class_field_initializer_name.visit([&](PropertyKey const& key) { key.visit_edges(visitor); }, [](auto&) {});
|
||||
}
|
||||
|
||||
SharedFunctionInstanceData::~SharedFunctionInstanceData() = default;
|
||||
|
|
|
|||
|
|
@ -226,17 +226,21 @@ void Animatable::clear_registered_transitions(Optional<CSS::PseudoElement> pseud
|
|||
|
||||
void Animatable::visit_edges(JS::Cell::Visitor& visitor)
|
||||
{
|
||||
auto& impl = ensure_impl();
|
||||
visitor.visit(impl.associated_animations);
|
||||
for (auto const& css_animation : impl.css_defined_animations) {
|
||||
if (m_impl)
|
||||
m_impl->visit_edges(visitor);
|
||||
}
|
||||
|
||||
void Animatable::Impl::visit_edges(JS::Cell::Visitor& visitor)
|
||||
{
|
||||
visitor.visit(associated_animations);
|
||||
for (auto const& css_animation : css_defined_animations) {
|
||||
if (css_animation)
|
||||
visitor.visit(*css_animation);
|
||||
}
|
||||
|
||||
for (auto const& transition : impl.transitions) {
|
||||
if (transition) {
|
||||
for (auto const& transition : transitions) {
|
||||
if (transition)
|
||||
visitor.visit(transition->associated_transitions);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -87,6 +87,8 @@ private:
|
|||
mutable Array<OwnPtr<Transition>, to_underlying(CSS::PseudoElement::KnownPseudoElementCount) + 1> transitions;
|
||||
|
||||
~Impl();
|
||||
|
||||
void visit_edges(JS::Cell::Visitor&);
|
||||
};
|
||||
Impl& ensure_impl() const;
|
||||
Transition* ensure_transition(Optional<CSS::PseudoElement>) const;
|
||||
|
|
|
|||
|
|
@ -442,6 +442,15 @@ struct ContentData {
|
|||
|
||||
Vector<Variant<String, NonnullRefPtr<ImageStyleValue>>> data;
|
||||
Optional<String> alt_text {};
|
||||
|
||||
void visit_edges(GC::Cell::Visitor& visitor) const
|
||||
{
|
||||
for (auto const& item : data) {
|
||||
if (auto* ptr = item.get_pointer<NonnullRefPtr<ImageStyleValue>>()) {
|
||||
(*ptr)->visit_edges(visitor);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
struct CounterData {
|
||||
|
|
@ -492,6 +501,11 @@ public:
|
|||
ComputedValues() = default;
|
||||
~ComputedValues() = default;
|
||||
|
||||
void visit_edges(GC::Cell::Visitor& visitor)
|
||||
{
|
||||
m_noninherited.visit_edges(visitor);
|
||||
}
|
||||
|
||||
AspectRatio aspect_ratio() const { return m_noninherited.aspect_ratio; }
|
||||
Float float_() const { return m_noninherited.float_; }
|
||||
Length border_spacing_horizontal() const { return m_inherited.border_spacing_horizontal; }
|
||||
|
|
@ -504,7 +518,7 @@ public:
|
|||
PreferredColorScheme color_scheme() const { return m_inherited.color_scheme; }
|
||||
ContentVisibility content_visibility() const { return m_inherited.content_visibility; }
|
||||
Vector<CursorData> const& cursor() const { return m_inherited.cursor; }
|
||||
ContentData content() const { return m_noninherited.content; }
|
||||
ContentData const& content() const { return m_noninherited.content; }
|
||||
PointerEvents pointer_events() const { return m_inherited.pointer_events; }
|
||||
Display display() const { return m_noninherited.display; }
|
||||
Display display_before_box_type_transformation() const { return m_noninherited.display_before_box_type_transformation; }
|
||||
|
|
@ -692,7 +706,7 @@ public:
|
|||
}
|
||||
|
||||
protected:
|
||||
struct {
|
||||
struct InheritedValues {
|
||||
Color caret_color { InitialValues::caret_color() };
|
||||
CSSPixels font_size { InitialValues::font_size() };
|
||||
RefPtr<Gfx::FontCascadeList const> font_list {};
|
||||
|
|
@ -753,9 +767,11 @@ protected:
|
|||
int math_depth { InitialValues::math_depth() };
|
||||
ScrollbarColorData scrollbar_color { InitialValues::scrollbar_color() };
|
||||
float stroke_opacity { InitialValues::stroke_opacity() };
|
||||
} m_inherited;
|
||||
};
|
||||
|
||||
struct {
|
||||
InheritedValues m_inherited;
|
||||
|
||||
struct NonInheritedValues {
|
||||
AspectRatio aspect_ratio { InitialValues::aspect_ratio() };
|
||||
Float float_ { InitialValues::float_() };
|
||||
Clear clear { InitialValues::clear() };
|
||||
|
|
@ -877,7 +893,26 @@ protected:
|
|||
Vector<CounterData, 0> counter_reset;
|
||||
Vector<CounterData, 0> counter_set;
|
||||
WillChange will_change { InitialValues::will_change() };
|
||||
} m_noninherited;
|
||||
|
||||
void visit_edges(GC::Cell::Visitor& visitor)
|
||||
{
|
||||
for (auto& layer : background_layers)
|
||||
layer.background_image->visit_edges(visitor);
|
||||
if (mask_image)
|
||||
mask_image->visit_edges(visitor);
|
||||
for (auto const& transform : transformations)
|
||||
transform->visit_edges(visitor);
|
||||
if (rotate)
|
||||
rotate->visit_edges(visitor);
|
||||
if (translate)
|
||||
translate->visit_edges(visitor);
|
||||
if (scale)
|
||||
scale->visit_edges(visitor);
|
||||
content.visit_edges(visitor);
|
||||
}
|
||||
};
|
||||
|
||||
NonInheritedValues m_noninherited;
|
||||
};
|
||||
|
||||
class ImmutableComputedValues final : public ComputedValues {
|
||||
|
|
|
|||
|
|
@ -26,6 +26,11 @@ struct HasResultCacheKey {
|
|||
CSS::Selector const* selector;
|
||||
GC::Ptr<DOM::Element const> element;
|
||||
|
||||
void visit_edges(GC::Cell::Visitor& visitor)
|
||||
{
|
||||
visitor.visit(element);
|
||||
}
|
||||
|
||||
bool operator==(HasResultCacheKey const&) const = default;
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -133,6 +133,8 @@ void StyleComputer::visit_edges(Visitor& visitor)
|
|||
{
|
||||
Base::visit_edges(visitor);
|
||||
visitor.visit(m_document);
|
||||
if (m_has_result_cache)
|
||||
visitor.visit(*m_has_result_cache);
|
||||
}
|
||||
|
||||
Optional<String> StyleComputer::user_agent_style_sheet_source(StringView name)
|
||||
|
|
|
|||
|
|
@ -22,9 +22,60 @@
|
|||
|
||||
namespace Web::CSS {
|
||||
|
||||
void RuleCaches::visit_edges(GC::Cell::Visitor& visitor)
|
||||
{
|
||||
main.visit_edges(visitor);
|
||||
for (auto& it : by_layer) {
|
||||
it.value->visit_edges(visitor);
|
||||
}
|
||||
}
|
||||
|
||||
void StyleScope::visit_edges(GC::Cell::Visitor& visitor)
|
||||
{
|
||||
visitor.visit(m_node);
|
||||
visitor.visit(m_user_style_sheet);
|
||||
for (auto& cache : m_pseudo_class_rule_cache) {
|
||||
if (cache)
|
||||
cache->visit_edges(visitor);
|
||||
}
|
||||
if (m_author_rule_cache)
|
||||
m_author_rule_cache->visit_edges(visitor);
|
||||
if (m_user_rule_cache)
|
||||
m_user_rule_cache->visit_edges(visitor);
|
||||
if (m_user_agent_rule_cache)
|
||||
m_user_agent_rule_cache->visit_edges(visitor);
|
||||
}
|
||||
|
||||
void MatchingRule::visit_edges(GC::Cell::Visitor& visitor)
|
||||
{
|
||||
visitor.visit(shadow_root);
|
||||
visitor.visit(rule);
|
||||
visitor.visit(sheet);
|
||||
}
|
||||
|
||||
void RuleCache::visit_edges(GC::Cell::Visitor& visitor)
|
||||
{
|
||||
auto visit_vector = [&](auto& vector) {
|
||||
for (auto& rule : vector)
|
||||
rule.visit_edges(visitor);
|
||||
};
|
||||
auto visit_map = [&](auto& map) {
|
||||
for (auto& [_, rules] : map) {
|
||||
visit_vector(rules);
|
||||
}
|
||||
};
|
||||
|
||||
visit_map(rules_by_id);
|
||||
visit_map(rules_by_class);
|
||||
visit_map(rules_by_tag_name);
|
||||
visit_map(rules_by_attribute_name);
|
||||
for (auto& rules : rules_by_pseudo_element) {
|
||||
visit_vector(rules);
|
||||
}
|
||||
visit_vector(root_rules);
|
||||
visit_vector(slotted_rules);
|
||||
visit_vector(part_rules);
|
||||
visit_vector(other_rules);
|
||||
}
|
||||
|
||||
StyleScope::StyleScope(GC::Ref<DOM::Node> node)
|
||||
|
|
|
|||
|
|
@ -38,6 +38,8 @@ struct MatchingRule {
|
|||
CSSStyleProperties const& declaration() const;
|
||||
SelectorList const& absolutized_selectors() const;
|
||||
FlyString const& qualified_layer_name() const;
|
||||
|
||||
void visit_edges(GC::Cell::Visitor&);
|
||||
};
|
||||
|
||||
struct RuleCache {
|
||||
|
|
@ -55,11 +57,15 @@ struct RuleCache {
|
|||
|
||||
void add_rule(MatchingRule const&, Optional<PseudoElement>, bool contains_root_pseudo_class);
|
||||
void for_each_matching_rules(DOM::AbstractElement, Function<IterationDecision(Vector<MatchingRule> const&)> callback) const;
|
||||
|
||||
void visit_edges(GC::Cell::Visitor&);
|
||||
};
|
||||
|
||||
struct RuleCaches {
|
||||
RuleCache main;
|
||||
HashMap<FlyString, NonnullOwnPtr<RuleCache>> by_layer;
|
||||
|
||||
void visit_edges(GC::Cell::Visitor&);
|
||||
};
|
||||
|
||||
struct SelectorInsights {
|
||||
|
|
|
|||
|
|
@ -3065,7 +3065,7 @@ ErrorOr<String> Node::name_or_description(NameOrDescription target, Document con
|
|||
// following the “iii. For each child node of the current node” code.
|
||||
if (auto before = element->get_pseudo_element_node(CSS::PseudoElement::Before)) {
|
||||
if (before->computed_values().content().alt_text.has_value()) {
|
||||
total_accumulated_text.append(before->computed_values().content().alt_text.release_value());
|
||||
total_accumulated_text.append(before->computed_values().content().alt_text.value());
|
||||
} else {
|
||||
for (auto& item : before->computed_values().content().data) {
|
||||
if (auto const* string = item.get_pointer<String>())
|
||||
|
|
@ -3125,7 +3125,7 @@ ErrorOr<String> Node::name_or_description(NameOrDescription target, Document con
|
|||
// NOTE: See step ii.b above.
|
||||
if (auto after = element->get_pseudo_element_node(CSS::PseudoElement::After)) {
|
||||
if (after->computed_values().content().alt_text.has_value()) {
|
||||
total_accumulated_text.append(after->computed_values().content().alt_text.release_value());
|
||||
total_accumulated_text.append(after->computed_values().content().alt_text.value());
|
||||
} else {
|
||||
for (auto& item : after->computed_values().content().data) {
|
||||
if (auto const* string = item.get_pointer<String>())
|
||||
|
|
|
|||
|
|
@ -111,6 +111,12 @@ public:
|
|||
Bindings::CanvasTextAlign text_align { Bindings::CanvasTextAlign::Start };
|
||||
Bindings::CanvasTextBaseline text_baseline { Bindings::CanvasTextBaseline::Alphabetic };
|
||||
Bindings::CanvasDirection direction { Bindings::CanvasDirection::Inherit };
|
||||
|
||||
void visit_edges(GC::Cell::Visitor& visitor)
|
||||
{
|
||||
fill_style.visit_edges(visitor);
|
||||
stroke_style.visit_edges(visitor);
|
||||
}
|
||||
};
|
||||
DrawingState& drawing_state() { return m_drawing_state; }
|
||||
DrawingState const& drawing_state() const { return m_drawing_state; }
|
||||
|
|
@ -122,9 +128,9 @@ public:
|
|||
|
||||
void visit_edges(GC::Cell::Visitor& visitor)
|
||||
{
|
||||
m_drawing_state.visit_edges(visitor);
|
||||
for (auto& state : m_drawing_state_stack) {
|
||||
state.fill_style.visit_edges(visitor);
|
||||
state.stroke_style.visit_edges(visitor);
|
||||
state.visit_edges(visitor);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -67,6 +67,7 @@ void HTMLFormElement::visit_edges(Cell::Visitor& visitor)
|
|||
visitor.visit(m_associated_elements);
|
||||
visitor.visit(m_planned_navigation);
|
||||
visitor.visit(m_rel_list);
|
||||
visitor.visit(m_past_names_map);
|
||||
}
|
||||
|
||||
// https://html.spec.whatwg.org/multipage/form-control-infrastructure.html#implicit-submission
|
||||
|
|
|
|||
|
|
@ -142,6 +142,11 @@ private:
|
|||
struct PastNameEntry {
|
||||
GC::Ptr<DOM::Node const> node;
|
||||
MonotonicTime insertion_time;
|
||||
|
||||
void visit_edges(GC::Cell::Visitor& visitor)
|
||||
{
|
||||
visitor.visit(node);
|
||||
}
|
||||
};
|
||||
HashMap<FlyString, PastNameEntry> mutable m_past_names_map;
|
||||
|
||||
|
|
|
|||
|
|
@ -199,6 +199,7 @@ void HTMLParser::visit_edges(Cell::Visitor& visitor)
|
|||
|
||||
m_stack_of_open_elements.visit_edges(visitor);
|
||||
m_list_of_active_formatting_elements.visit_edges(visitor);
|
||||
m_tokenizer.visit_edges(visitor);
|
||||
}
|
||||
|
||||
void HTMLParser::initialize(JS::Realm& realm)
|
||||
|
|
|
|||
|
|
@ -3005,4 +3005,9 @@ String HTMLTokenizer::consume_current_builder()
|
|||
return string;
|
||||
}
|
||||
|
||||
void HTMLTokenizer::visit_edges(GC::Cell::Visitor& visitor)
|
||||
{
|
||||
visitor.visit(m_parser);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@
|
|||
#include <AK/StringBuilder.h>
|
||||
#include <AK/StringView.h>
|
||||
#include <AK/Types.h>
|
||||
#include <LibGC/Cell.h>
|
||||
#include <LibGC/Ptr.h>
|
||||
#include <LibWeb/Export.h>
|
||||
#include <LibWeb/Forward.h>
|
||||
|
|
@ -147,6 +148,8 @@ public:
|
|||
|
||||
void parser_did_run(Badge<HTMLParser>);
|
||||
|
||||
void visit_edges(GC::Cell::Visitor&);
|
||||
|
||||
private:
|
||||
void skip(size_t count);
|
||||
Optional<u32> next_code_point(StopAtInsertionPoint);
|
||||
|
|
|
|||
|
|
@ -477,6 +477,8 @@ void NodeWithStyle::visit_edges(Visitor& visitor)
|
|||
|
||||
if (m_list_style_image && m_list_style_image->is_image())
|
||||
m_list_style_image->as_image().visit_edges(visitor);
|
||||
|
||||
m_computed_values->visit_edges(visitor);
|
||||
}
|
||||
|
||||
void NodeWithStyle::apply_style(CSS::ComputedProperties const& computed_style)
|
||||
|
|
|
|||
|
|
@ -250,6 +250,7 @@ private:
|
|||
{
|
||||
Base::visit_edges(visitor);
|
||||
visitor.visit(m_layout_node);
|
||||
m_image->visit_edges(visitor);
|
||||
}
|
||||
|
||||
virtual void image_provider_visit_edges(Visitor& visitor) const override
|
||||
|
|
|
|||
|
|
@ -1607,6 +1607,8 @@ void EventHandler::visit_edges(JS::Cell::Visitor& visitor) const
|
|||
|
||||
if (m_mouse_selection_target)
|
||||
visitor.visit(m_mouse_selection_target->as_cell());
|
||||
|
||||
visitor.visit(m_navigable);
|
||||
}
|
||||
|
||||
Unicode::Segmenter& EventHandler::word_segmenter()
|
||||
|
|
|
|||
|
|
@ -81,6 +81,12 @@ PaintableBox::~PaintableBox()
|
|||
{
|
||||
}
|
||||
|
||||
void PaintableBox::visit_edges(Cell::Visitor& visitor)
|
||||
{
|
||||
Base::visit_edges(visitor);
|
||||
visitor.visit(m_stacking_context);
|
||||
}
|
||||
|
||||
PaintableWithLines::PaintableWithLines(Layout::BlockContainer const& layout_box)
|
||||
: PaintableBox(layout_box)
|
||||
{
|
||||
|
|
@ -567,7 +573,7 @@ void PaintableBox::paint_inspector_overlay_internal(DisplayListRecordingContext&
|
|||
context.display_list_recorder().draw_text(size_text_device_rect, size_text, font->with_size(font->point_size() * context.device_pixels_per_css_pixel()), Gfx::TextAlignment::Center, context.palette().color(Gfx::ColorRole::TooltipText));
|
||||
}
|
||||
|
||||
void PaintableBox::set_stacking_context(NonnullOwnPtr<StackingContext> stacking_context)
|
||||
void PaintableBox::set_stacking_context(GC::Ref<StackingContext> stacking_context)
|
||||
{
|
||||
m_stacking_context = move(stacking_context);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -39,7 +39,7 @@ public:
|
|||
|
||||
StackingContext* stacking_context() { return m_stacking_context; }
|
||||
StackingContext const* stacking_context() const { return m_stacking_context; }
|
||||
void set_stacking_context(NonnullOwnPtr<StackingContext>);
|
||||
void set_stacking_context(GC::Ref<StackingContext>);
|
||||
void invalidate_stacking_context();
|
||||
|
||||
virtual Optional<CSSPixelRect> get_masking_area() const;
|
||||
|
|
@ -269,6 +269,8 @@ protected:
|
|||
explicit PaintableBox(Layout::Box const&);
|
||||
explicit PaintableBox(Layout::InlineNode const&);
|
||||
|
||||
virtual void visit_edges(Visitor&) override;
|
||||
|
||||
virtual void paint_border(DisplayListRecordingContext&) const;
|
||||
virtual void paint_backdrop_filter(DisplayListRecordingContext&) const;
|
||||
virtual void paint_background(DisplayListRecordingContext&) const;
|
||||
|
|
@ -309,7 +311,7 @@ private:
|
|||
bool scrollbar_contains_mouse_position(ScrollDirection, CSSPixelPoint);
|
||||
void scroll_to_mouse_position(CSSPixelPoint);
|
||||
|
||||
OwnPtr<StackingContext> m_stacking_context;
|
||||
GC::Ptr<StackingContext> m_stacking_context;
|
||||
|
||||
Optional<OverflowData> m_overflow_data;
|
||||
|
||||
|
|
|
|||
|
|
@ -24,6 +24,8 @@
|
|||
|
||||
namespace Web::Painting {
|
||||
|
||||
GC_DEFINE_ALLOCATOR(StackingContext);
|
||||
|
||||
static void paint_node(Paintable const& paintable, DisplayListRecordingContext& context, PaintPhase phase)
|
||||
{
|
||||
TemporaryChange save_nesting_level(context.display_list_recorder().m_save_nesting_level, 0);
|
||||
|
|
@ -42,7 +44,7 @@ StackingContext::StackingContext(PaintableBox& paintable, StackingContext* paren
|
|||
{
|
||||
VERIFY(m_parent != this);
|
||||
if (m_parent)
|
||||
m_parent->m_children.append(this);
|
||||
m_parent->m_children.append(*this);
|
||||
}
|
||||
|
||||
void StackingContext::sort()
|
||||
|
|
@ -55,10 +57,20 @@ void StackingContext::sort()
|
|||
return a_z_index < b_z_index;
|
||||
});
|
||||
|
||||
for (auto* child : m_children)
|
||||
for (auto child : m_children)
|
||||
child->sort();
|
||||
}
|
||||
|
||||
void StackingContext::visit_edges(Visitor& visitor)
|
||||
{
|
||||
Base::visit_edges(visitor);
|
||||
visitor.visit(m_paintable);
|
||||
visitor.visit(m_non_positioned_floating_descendants);
|
||||
visitor.visit(m_positioned_descendants_and_stacking_contexts_with_stack_level_0);
|
||||
visitor.visit(m_parent);
|
||||
visitor.visit(m_children);
|
||||
}
|
||||
|
||||
void StackingContext::set_last_paint_generation_id(u64 generation_id)
|
||||
{
|
||||
if (m_last_paint_generation_id.has_value() && m_last_paint_generation_id.value() >= generation_id) {
|
||||
|
|
@ -238,7 +250,7 @@ void StackingContext::paint_internal(DisplayListRecordingContext& context) const
|
|||
// Here, we treat non-positioned stacking contexts as if they were positioned, because CSS 2.0 spec does not
|
||||
// account for new properties like `transform` and `opacity` that can create stacking contexts.
|
||||
// https://github.com/w3c/csswg-drafts/issues/2717
|
||||
for (auto* child : m_children) {
|
||||
for (auto child : m_children) {
|
||||
if (child->paintable_box().computed_values().z_index().has_value() && child->paintable_box().computed_values().z_index().value() < 0)
|
||||
paint_child(context, *child);
|
||||
}
|
||||
|
|
@ -271,7 +283,7 @@ void StackingContext::paint_internal(DisplayListRecordingContext& context) const
|
|||
// Here, we treat non-positioned stacking contexts as if they were positioned, because CSS 2.0 spec does not
|
||||
// account for new properties like `transform` and `opacity` that can create stacking contexts.
|
||||
// https://github.com/w3c/csswg-drafts/issues/2717
|
||||
for (auto* child : m_children) {
|
||||
for (auto child : m_children) {
|
||||
if (child->paintable_box().computed_values().z_index().has_value() && child->paintable_box().computed_values().z_index().value() >= 1)
|
||||
paint_child(context, *child);
|
||||
}
|
||||
|
|
@ -414,7 +426,7 @@ TraversalDecision StackingContext::hit_test(CSSPixelPoint position, HitTestType
|
|||
|
||||
// 7. the child stacking contexts with positive stack levels (least positive first).
|
||||
// NOTE: Hit testing follows reverse painting order, that's why the conditions here are reversed.
|
||||
for (auto const* child : m_children.in_reverse()) {
|
||||
for (auto const child : m_children.in_reverse()) {
|
||||
if (child->paintable_box().computed_values().z_index().value_or(0) <= 0)
|
||||
break;
|
||||
if (child->hit_test(transformed_position, type, callback) == TraversalDecision::Break)
|
||||
|
|
@ -464,7 +476,7 @@ TraversalDecision StackingContext::hit_test(CSSPixelPoint position, HitTestType
|
|||
|
||||
// 2. the child stacking contexts with negative stack levels (most negative first).
|
||||
// NOTE: Hit testing follows reverse painting order, that's why the conditions here are reversed.
|
||||
for (auto const* child : m_children.in_reverse()) {
|
||||
for (auto const child : m_children.in_reverse()) {
|
||||
if (child->paintable_box().computed_values().z_index().value_or(0) >= 0)
|
||||
break;
|
||||
if (child->hit_test(transformed_position, type, callback) == TraversalDecision::Break)
|
||||
|
|
|
|||
|
|
@ -7,12 +7,15 @@
|
|||
#pragma once
|
||||
|
||||
#include <AK/Vector.h>
|
||||
#include <LibGC/CellAllocator.h>
|
||||
#include <LibWeb/Export.h>
|
||||
#include <LibWeb/Painting/Paintable.h>
|
||||
|
||||
namespace Web::Painting {
|
||||
|
||||
class WEB_API StackingContext {
|
||||
class WEB_API StackingContext final : public GC::Cell {
|
||||
GC_CELL(StackingContext, GC::Cell);
|
||||
GC_DECLARE_ALLOCATOR(StackingContext);
|
||||
friend class ViewportPaintable;
|
||||
|
||||
public:
|
||||
|
|
@ -46,10 +49,12 @@ public:
|
|||
|
||||
void set_last_paint_generation_id(u64 generation_id);
|
||||
|
||||
virtual void visit_edges(Visitor&) override;
|
||||
|
||||
private:
|
||||
GC::Ref<PaintableBox> m_paintable;
|
||||
StackingContext* const m_parent { nullptr };
|
||||
Vector<StackingContext*> m_children;
|
||||
GC::Ptr<StackingContext> m_parent;
|
||||
Vector<GC::Ref<StackingContext>> m_children;
|
||||
size_t m_index_in_tree_order { 0 };
|
||||
Optional<u64> m_last_paint_generation_id;
|
||||
|
||||
|
|
|
|||
|
|
@ -38,7 +38,7 @@ void ViewportPaintable::build_stacking_context_tree_if_needed()
|
|||
|
||||
void ViewportPaintable::build_stacking_context_tree()
|
||||
{
|
||||
set_stacking_context(make<StackingContext>(*this, nullptr, 0));
|
||||
set_stacking_context(heap().allocate<StackingContext>(*this, nullptr, 0));
|
||||
|
||||
size_t index_in_tree_order = 1;
|
||||
for_each_in_subtree_of_type<PaintableBox>([&](auto& paintable_box) {
|
||||
|
|
@ -54,7 +54,7 @@ void ViewportPaintable::build_stacking_context_tree()
|
|||
return TraversalDecision::Continue;
|
||||
}
|
||||
VERIFY(parent_context);
|
||||
paintable_box.set_stacking_context(make<StackingContext>(paintable_box, parent_context, index_in_tree_order++));
|
||||
paintable_box.set_stacking_context(heap().allocate<StackingContext>(paintable_box, parent_context, index_in_tree_order++));
|
||||
return TraversalDecision::Continue;
|
||||
});
|
||||
|
||||
|
|
|
|||
|
|
@ -32,6 +32,8 @@ void XPathResult::visit_edges(Cell::Visitor& visitor)
|
|||
{
|
||||
Base::visit_edges(visitor);
|
||||
visitor.visit(m_node_set);
|
||||
if (!m_node_set_iter.is_end())
|
||||
visitor.visit(*m_node_set_iter);
|
||||
}
|
||||
|
||||
XPathResult::~XPathResult() = default;
|
||||
|
|
|
|||
Loading…
Reference in a new issue