Store ResizeObservation targets weakly, matching Blink and WebKit. A ResizeObserver can be kept alive by the document while it has observed targets, but the observation itself should not keep a removed target and its subtree alive forever. Prune dead observations before gathering active resize observations. Snapshot the document observer list before pruning so unregistering an observer cannot mutate the intrusive list being iterated. Keep gathered active targets rooted while broadcasting callbacks, since an earlier callback can remove a later target and trigger GC before delivery. Expose an internals helper to force environment-bound test objects to be treated as garbage. Add text coverage for both pruning a dead observer during gather and GC during an earlier resize observer callback.
63 lines
2.1 KiB
C++
63 lines
2.1 KiB
C++
/*
|
|
* Copyright (c) 2021, Andreas Kling <andreas@ladybird.org>
|
|
* Copyright (c) 2024-2025, Aliaksandr Kalenik <kalenik.aliaksandr@gmail.com>
|
|
*
|
|
* SPDX-License-Identifier: BSD-2-Clause
|
|
*/
|
|
|
|
#pragma once
|
|
|
|
#include <LibWeb/Bindings/PlatformObject.h>
|
|
#include <LibWeb/Bindings/ResizeObserver.h>
|
|
#include <LibWeb/ResizeObserver/ResizeObservation.h>
|
|
#include <LibWeb/ResizeObserver/ResizeObserverEntry.h>
|
|
|
|
namespace Web::ResizeObserver {
|
|
|
|
// https://drafts.csswg.org/resize-observer-1/#resize-observer-interface
|
|
class ResizeObserver : public Bindings::PlatformObject {
|
|
WEB_PLATFORM_OBJECT(ResizeObserver, Bindings::PlatformObject);
|
|
GC_DECLARE_ALLOCATOR(ResizeObserver);
|
|
|
|
public:
|
|
static constexpr bool OVERRIDES_FINALIZE = true;
|
|
|
|
static WebIDL::ExceptionOr<GC::Ref<ResizeObserver>> construct_impl(JS::Realm&, WebIDL::CallbackType* callback);
|
|
|
|
virtual ~ResizeObserver() override;
|
|
|
|
void observe(DOM::Element& target, Bindings::ResizeObserverOptions);
|
|
void unobserve(DOM::Element& target);
|
|
void disconnect();
|
|
|
|
void invoke_callback(ReadonlySpan<GC::Ref<ResizeObserverEntry>> entries) const;
|
|
|
|
Vector<GC::Ref<ResizeObservation>>& observation_targets() { return m_observation_targets; }
|
|
Vector<GC::Ref<ResizeObservation>>& active_targets() { return m_active_targets; }
|
|
Vector<GC::Ref<ResizeObservation>>& skipped_targets() { return m_skipped_targets; }
|
|
void remove_dead_observations();
|
|
|
|
private:
|
|
explicit ResizeObserver(JS::Realm&, WebIDL::CallbackType* callback);
|
|
|
|
virtual void initialize(JS::Realm&) override;
|
|
virtual void visit_edges(JS::Cell::Visitor&) override;
|
|
virtual void finalize() override;
|
|
|
|
void unregister_observer_if_needed();
|
|
|
|
GC::Ptr<WebIDL::CallbackType> m_callback;
|
|
Vector<GC::Ref<ResizeObservation>> m_observation_targets;
|
|
Vector<GC::Ref<ResizeObservation>> m_active_targets;
|
|
Vector<GC::Ref<ResizeObservation>> m_skipped_targets;
|
|
|
|
// AD-HOC: This is the document where we've registered the observer.
|
|
GC::Weak<DOM::Document> m_document;
|
|
|
|
IntrusiveListNode<ResizeObserver> m_list_node;
|
|
|
|
public:
|
|
using ResizeObserversList = IntrusiveList<&ResizeObserver::m_list_node>;
|
|
};
|
|
|
|
}
|