ladybird/Userland/Libraries/LibWeb/ResizeObserver/ResizeObserver.cpp
Aliaksandr Kalenik fb8edcea00 LibWeb: Flesh out implementation of ResizeObserver interfaces
Adds the initial implementation for interfaces defined in the
ResizeObserver specification. These interfaces will be used to
construct and send observation events in the upcoming changes.
2024-02-20 10:55:10 +01:00

108 lines
3.6 KiB
C++

/*
* Copyright (c) 2021, Andreas Kling <kling@serenityos.org>
* Copyright (c) 2024, Aliaksandr Kalenik <kalenik.aliaksandr@gmail.com>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#include <LibWeb/Bindings/Intrinsics.h>
#include <LibWeb/DOM/Document.h>
#include <LibWeb/HTML/Scripting/ExceptionReporter.h>
#include <LibWeb/HTML/Window.h>
#include <LibWeb/ResizeObserver/ResizeObserver.h>
#include <LibWeb/WebIDL/AbstractOperations.h>
namespace Web::ResizeObserver {
JS_DEFINE_ALLOCATOR(ResizeObserver);
// https://drafts.csswg.org/resize-observer/#dom-resizeobserver-resizeobserver
WebIDL::ExceptionOr<JS::NonnullGCPtr<ResizeObserver>> ResizeObserver::construct_impl(JS::Realm& realm, WebIDL::CallbackType* callback)
{
return realm.heap().allocate<ResizeObserver>(realm, realm, callback);
}
ResizeObserver::ResizeObserver(JS::Realm& realm, WebIDL::CallbackType* callback)
: PlatformObject(realm)
, m_callback(callback)
{
auto navigable = verify_cast<HTML::Window>(HTML::relevant_global_object(*this)).navigable();
m_document = navigable->active_document().ptr();
m_document->register_resize_observer({}, *this);
}
ResizeObserver::~ResizeObserver() = default;
void ResizeObserver::initialize(JS::Realm& realm)
{
Base::initialize(realm);
set_prototype(&Bindings::ensure_web_prototype<Bindings::ResizeObserverPrototype>(realm, "ResizeObserver"_fly_string));
}
void ResizeObserver::visit_edges(JS::Cell::Visitor& visitor)
{
Base::visit_edges(visitor);
visitor.visit(m_callback);
for (auto& observation : m_observation_targets)
visitor.visit(observation);
for (auto& observation : m_active_targets)
visitor.visit(observation);
for (auto& observation : m_skipped_targets)
visitor.visit(observation);
}
void ResizeObserver::finalize()
{
if (m_document)
m_document->unregister_resize_observer({}, *this);
}
// https://drafts.csswg.org/resize-observer-1/#dom-resizeobserver-observe
void ResizeObserver::observe(DOM::Element& target, ResizeObserverOptions options)
{
// 1. If target is in [[observationTargets]] slot, call unobserve() with argument target.
auto observation = m_observation_targets.find_if([&](auto& observation) { return observation->target().ptr() == &target; });
if (!observation.is_end())
unobserve(target);
// 2. Let observedBox be the value of the box dictionary member of options.
auto observed_box = options.box;
// 3. Let resizeObservation be new ResizeObservation(target, observedBox).
auto resize_observation = MUST(ResizeObservation::create(realm(), target, observed_box));
// 4. Add the resizeObservation to the [[observationTargets]] slot.
m_observation_targets.append(resize_observation);
}
// https://drafts.csswg.org/resize-observer/#dom-resizeobserver-unobserve
void ResizeObserver::unobserve(DOM::Element& target)
{
// FIXME: Implement
(void)target;
}
// https://drafts.csswg.org/resize-observer/#dom-resizeobserver-disconnect
void ResizeObserver::disconnect()
{
// FIXME: Implement
}
void ResizeObserver::invoke_callback(Vector<JS::NonnullGCPtr<ResizeObserverEntry>>& entries) const
{
auto& callback = *m_callback;
auto& realm = callback.callback_context->realm();
auto wrapped_records = MUST(JS::Array::create(realm, 0));
for (size_t i = 0; i < entries.size(); ++i) {
auto& record = entries.at(i);
auto property_index = JS::PropertyKey { i };
MUST(wrapped_records->create_data_property(property_index, record.ptr()));
}
auto result = WebIDL::invoke_callback(callback, JS::js_undefined(), wrapped_records);
if (result.is_abrupt())
HTML::report_exception(result, realm);
}
}