ladybird/Libraries/LibWeb/CSS/StyleValues/ImageSetStyleValue.h
Aliaksandr Kalenik 35b582048e LibWeb: Avoid stale CSS image style GC pointers
ImageStyleValue stored GC-managed request, stylesheet, and animation
timer references as strong GC::Ptr fields even though image style values
are refcounted objects. When such a style value outlives the GC object
that normally visits it, those fields can keep stale pointers after GC
collects the referents. On Steam this allowed a stale image resource
request to be read as unrelated image data, making carousel SVG arrows
render at the wrong size.

Store these back references as GC::Weak instead. Reachable style values
still use live requests, stylesheets, and timers normally, but detached
values observe null after the GC collects the referent and can reload or
skip the now-dead association instead of dereferencing reused GC memory.
Keep a local timer handle while installing the timeout callback so setup
does not rely on the weak member.

With the image style values no longer hiding strong GC edges, remove the
obsolete IGNORE_GC annotation from CSSStyleSheet's pending image list.
2026-05-25 11:06:23 +02:00

62 lines
2.1 KiB
C++

/*
* Copyright (c) 2026-present, the Ladybird developers.
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#pragma once
#include <AK/Optional.h>
#include <AK/String.h>
#include <AK/Vector.h>
#include <LibGC/Weak.h>
#include <LibWeb/CSS/StyleValues/AbstractImageStyleValue.h>
namespace Web::CSS {
class ImageSetStyleValue final : public AbstractImageStyleValue {
using Base = AbstractImageStyleValue;
public:
struct Option {
NonnullRefPtr<AbstractImageStyleValue const> image;
NonnullRefPtr<StyleValue const> resolution;
Optional<String> type;
};
static ValueComparingNonnullRefPtr<ImageSetStyleValue const> create(Vector<Option>);
virtual ~ImageSetStyleValue() override = default;
virtual void visit_edges(JS::Cell::Visitor&) const override;
virtual void serialize(StringBuilder&, SerializationMode) const override;
virtual bool equals(StyleValue const& other) const override;
virtual bool is_computationally_independent() const override;
virtual void load_any_resources(DOM::Document&) override;
virtual Optional<CSSPixels> natural_width() const override;
virtual Optional<CSSPixels> natural_height() const override;
virtual Optional<CSSPixelFraction> natural_aspect_ratio() const override;
virtual void resolve_for_size(Layout::NodeWithStyle const&, CSSPixelSize) const override;
virtual bool is_paintable() const override;
virtual void paint(DisplayListRecordingContext&, DevicePixelRect const&, ImageRendering) const override;
virtual Optional<Gfx::Color> color_if_single_pixel_bitmap() const override;
AbstractImageStyleValue const* selected_image() const { return m_selected_image; }
private:
explicit ImageSetStyleValue(Vector<Option>);
virtual void set_style_sheet(GC::Ptr<CSSStyleSheet>) override;
virtual ValueComparingNonnullRefPtr<StyleValue const> absolutized(ComputationContext const&) const override;
AbstractImageStyleValue const* select_image(double device_pixels_per_css_pixel) const;
Vector<Option> m_options;
GC::Weak<CSSStyleSheet> m_style_sheet;
mutable AbstractImageStyleValue const* m_selected_image { nullptr };
};
}