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.
This commit is contained in:
Aliaksandr Kalenik 2024-02-19 05:06:39 +01:00 committed by Andreas Kling
parent fb4c632309
commit fb8edcea00
Notes: sideshowbarker 2024-07-17 07:14:09 +09:00
15 changed files with 494 additions and 13 deletions

View File

@ -539,7 +539,10 @@ set(SOURCES
ReferrerPolicy/AbstractOperations.cpp
ReferrerPolicy/ReferrerPolicy.cpp
RequestIdleCallback/IdleDeadline.cpp
ResizeObserver/ResizeObservation.cpp
ResizeObserver/ResizeObserver.cpp
ResizeObserver/ResizeObserverEntry.cpp
ResizeObserver/ResizeObserverSize.cpp
SecureContexts/AbstractOperations.cpp
SRI/SRI.cpp
Streams/AbstractOperations.cpp

View File

@ -95,6 +95,8 @@
#include <LibWeb/Page/Page.h>
#include <LibWeb/Painting/ViewportPaintable.h>
#include <LibWeb/PermissionsPolicy/AutoplayAllowlist.h>
#include <LibWeb/ResizeObserver/ResizeObserver.h>
#include <LibWeb/ResizeObserver/ResizeObserverEntry.h>
#include <LibWeb/SVG/SVGElement.h>
#include <LibWeb/SVG/SVGTitleElement.h>
#include <LibWeb/SVG/TagNames.h>
@ -453,6 +455,9 @@ void Document::visit_edges(Cell::Visitor& visitor)
for (auto& observer : m_intersection_observers)
visitor.visit(observer);
for (auto& observer : m_resize_observers)
visitor.visit(observer);
for (auto& image : m_shared_image_requests)
visitor.visit(image.value);
@ -3161,6 +3166,18 @@ void Document::unregister_intersection_observer(Badge<IntersectionObserver::Inte
VERIFY(was_removed);
}
void Document::register_resize_observer(Badge<ResizeObserver::ResizeObserver>, ResizeObserver::ResizeObserver& observer)
{
m_resize_observers.append(observer);
}
void Document::unregister_resize_observer(Badge<ResizeObserver::ResizeObserver>, ResizeObserver::ResizeObserver& observer)
{
m_resize_observers.remove_first_matching([&](auto& registered_observer) {
return registered_observer.ptr() == &observer;
});
}
// https://www.w3.org/TR/intersection-observer/#queue-an-intersection-observer-task
void Document::queue_intersection_observer_task()
{

View File

@ -525,6 +525,9 @@ public:
void register_intersection_observer(Badge<IntersectionObserver::IntersectionObserver>, IntersectionObserver::IntersectionObserver&);
void unregister_intersection_observer(Badge<IntersectionObserver::IntersectionObserver>, IntersectionObserver::IntersectionObserver&);
void register_resize_observer(Badge<ResizeObserver::ResizeObserver>, ResizeObserver::ResizeObserver&);
void unregister_resize_observer(Badge<ResizeObserver::ResizeObserver>, ResizeObserver::ResizeObserver&);
void run_the_update_intersection_observations_steps(HighResolutionTime::DOMHighResTimeStamp time);
void start_intersection_observing_a_lazy_loading_element(Element&);
@ -782,6 +785,8 @@ private:
// Each Document has a lazy load intersection observer, initially set to null but can be set to an IntersectionObserver instance.
JS::GCPtr<IntersectionObserver::IntersectionObserver> m_lazy_load_intersection_observer;
Vector<JS::NonnullGCPtr<ResizeObserver::ResizeObserver>> m_resize_observers;
// https://html.spec.whatwg.org/multipage/semantics.html#will-declaratively-refresh
// A Document object has an associated will declaratively refresh (a boolean). It is initially false.
bool m_will_declaratively_refresh { false };

View File

@ -0,0 +1,54 @@
/*
* Copyright (c) 2024, Aliaksandr Kalenik <kalenik.aliaksandr@gmail.com>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#include <LibJS/Heap/Heap.h>
#include <LibWeb/Bindings/ExceptionOrUtils.h>
#include <LibWeb/DOM/Element.h>
#include <LibWeb/Painting/PaintableBox.h>
#include <LibWeb/ResizeObserver/ResizeObservation.h>
namespace Web::ResizeObserver {
JS_DEFINE_ALLOCATOR(ResizeObservation);
WebIDL::ExceptionOr<JS::NonnullGCPtr<ResizeObservation>> ResizeObservation::create(JS::Realm& realm, DOM::Element& target, Bindings::ResizeObserverBoxOptions observed_box)
{
return realm.heap().allocate<ResizeObservation>(realm, realm, target, observed_box);
}
ResizeObservation::ResizeObservation(JS::Realm& realm, DOM::Element& target, Bindings::ResizeObserverBoxOptions observed_box)
: m_realm(realm)
, m_target(target)
, m_observed_box(observed_box)
{
auto computed_size = realm.heap().allocate<ResizeObserverSize>(realm, realm);
m_last_reported_sizes.append(computed_size);
}
void ResizeObservation::visit_edges(JS::Cell::Visitor& visitor)
{
Base::visit_edges(visitor);
visitor.visit(m_target);
for (auto& size : m_last_reported_sizes)
visitor.visit(size);
}
// https://drafts.csswg.org/resize-observer-1/#dom-resizeobservation-isactive
bool ResizeObservation::is_active()
{
// 1. Set currentSize by calculate box size given target and observedBox.
auto current_size = ResizeObserverSize::calculate_box_size(m_realm, m_target, m_observed_box);
// 2. Return true if currentSize is not equal to the first entry in this.lastReportedSizes.
VERIFY(!m_last_reported_sizes.is_empty());
if (!m_last_reported_sizes.first()->equals(*current_size))
return true;
// 3. Return false.
return false;
}
}

View File

@ -0,0 +1,41 @@
/*
* Copyright (c) 2024, Aliaksandr Kalenik <kalenik.aliaksandr@gmail.com>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#pragma once
#include <LibWeb/Bindings/PlatformObject.h>
#include <LibWeb/Bindings/ResizeObserverPrototype.h>
#include <LibWeb/ResizeObserver/ResizeObserverSize.h>
namespace Web::ResizeObserver {
// https://drafts.csswg.org/resize-observer-1/#resize-observation-interface
class ResizeObservation : public JS::Cell {
JS_CELL(ResizeObservation, JS::Cell);
JS_DECLARE_ALLOCATOR(ResizeObservation);
public:
static WebIDL::ExceptionOr<JS::NonnullGCPtr<ResizeObservation>> create(JS::Realm&, DOM::Element&, Bindings::ResizeObserverBoxOptions);
bool is_active();
JS::NonnullGCPtr<DOM::Element> target() const { return m_target; }
Bindings::ResizeObserverBoxOptions observed_box() const { return m_observed_box; }
Vector<JS::NonnullGCPtr<ResizeObserverSize>>& last_reported_sizes() { return m_last_reported_sizes; }
explicit ResizeObservation(JS::Realm& realm, DOM::Element& target, Bindings::ResizeObserverBoxOptions observed_box);
private:
virtual void visit_edges(JS::Cell::Visitor&) override;
JS::NonnullGCPtr<JS::Realm> m_realm;
JS::NonnullGCPtr<DOM::Element> m_target;
Bindings::ResizeObserverBoxOptions m_observed_box;
Vector<JS::NonnullGCPtr<ResizeObserverSize>> m_last_reported_sizes;
};
}

View File

@ -1,12 +1,16 @@
/*
* 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/Element.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 {
@ -15,14 +19,16 @@ 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)
{
// FIXME: Implement
(void)callback;
return realm.heap().allocate<ResizeObserver>(realm, realm);
return realm.heap().allocate<ResizeObserver>(realm, realm, callback);
}
ResizeObserver::ResizeObserver(JS::Realm& realm)
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;
@ -33,12 +39,40 @@ void ResizeObserver::initialize(JS::Realm& realm)
set_prototype(&Bindings::ensure_web_prototype<Bindings::ResizeObserverPrototype>(realm, "ResizeObserver"_fly_string));
}
// https://drafts.csswg.org/resize-observer/#dom-resizeobserver-observe
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)
{
// FIXME: Implement
(void)target;
(void)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
@ -54,4 +88,21 @@ 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);
}
}

View File

@ -1,5 +1,6 @@
/*
* Copyright (c) 2021, Andreas Kling <kling@serenityos.org>
* Copyright (c) 2024, Aliaksandr Kalenik <kalenik.aliaksandr@gmail.com>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
@ -7,6 +8,8 @@
#pragma once
#include <LibWeb/Bindings/PlatformObject.h>
#include <LibWeb/ResizeObserver/ResizeObservation.h>
#include <LibWeb/ResizeObserver/ResizeObserverEntry.h>
namespace Web::ResizeObserver {
@ -14,7 +17,7 @@ struct ResizeObserverOptions {
Bindings::ResizeObserverBoxOptions box;
};
// https://drafts.csswg.org/resize-observer/#resize-observer-interface
// https://drafts.csswg.org/resize-observer-1/#resize-observer-interface
class ResizeObserver : public Bindings::PlatformObject {
WEB_PLATFORM_OBJECT(ResizeObserver, Bindings::PlatformObject);
JS_DECLARE_ALLOCATOR(ResizeObserver);
@ -28,10 +31,26 @@ public:
void unobserve(DOM::Element& target);
void disconnect();
void invoke_callback(Vector<JS::NonnullGCPtr<ResizeObserverEntry>>& entries) const;
Vector<JS::NonnullGCPtr<ResizeObservation>>& observation_targets() { return m_observation_targets; }
Vector<JS::NonnullGCPtr<ResizeObservation>>& active_targets() { return m_active_targets; }
Vector<JS::NonnullGCPtr<ResizeObservation>>& skipped_targets() { return m_skipped_targets; }
private:
explicit ResizeObserver(JS::Realm&);
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;
JS::GCPtr<WebIDL::CallbackType> m_callback;
Vector<JS::NonnullGCPtr<ResizeObservation>> m_observation_targets;
Vector<JS::NonnullGCPtr<ResizeObservation>> m_active_targets;
Vector<JS::NonnullGCPtr<ResizeObservation>> m_skipped_targets;
// AD-HOC: This is the document where we've registered the observer.
WeakPtr<DOM::Document> m_document;
};
}

View File

@ -1,6 +1,6 @@
#import <DOM/Element.idl>
// https://drafts.csswg.org/resize-observer/#resize-observer-interface
// https://drafts.csswg.org/resize-observer-1/#resize-observer-interface
[Exposed=(Window)]
interface ResizeObserver {
@ -19,5 +19,5 @@ dictionary ResizeObserverOptions {
ResizeObserverBoxOptions box = "content-box";
};
// https://drafts.csswg.org/resize-observer/#resize-observer-callback
// https://drafts.csswg.org/resize-observer-1/#resize-observer-callback
callback ResizeObserverCallback = undefined (sequence<ResizeObserverEntry> entries, ResizeObserver observer);

View File

@ -0,0 +1,102 @@
/*
* Copyright (c) 2024, Aliaksandr Kalenik <kalenik.aliaksandr@gmail.com>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#include <LibJS/Heap/Heap.h>
#include <LibWeb/Bindings/ExceptionOrUtils.h>
#include <LibWeb/Bindings/Intrinsics.h>
#include <LibWeb/Bindings/ResizeObserverEntryPrototype.h>
#include <LibWeb/Painting/PaintableBox.h>
#include <LibWeb/ResizeObserver/ResizeObserverEntry.h>
namespace Web::ResizeObserver {
JS_DEFINE_ALLOCATOR(ResizeObserverEntry);
// https://drafts.csswg.org/resize-observer-1/#create-and-populate-resizeobserverentry-h
WebIDL::ExceptionOr<JS::NonnullGCPtr<ResizeObserverEntry>> ResizeObserverEntry::create_and_populate(JS::Realm& realm, DOM::Element& target)
{
// 1. Let this be a new ResizeObserverEntry.
// 2. Set this.target slot to target.
auto resize_observer_entry = realm.heap().allocate<ResizeObserverEntry>(realm, realm, target);
// 3. Set this.borderBoxSize slot to result of calculating box size given target and observedBox of "border-box".
auto border_box_size = ResizeObserverSize::calculate_box_size(realm, target, Bindings::ResizeObserverBoxOptions::BorderBox);
resize_observer_entry->m_border_box_size.append(border_box_size);
// 4. Set this.contentBoxSize slot to result of calculating box size given target and observedBox of "content-box".
auto content_box_size = ResizeObserverSize::calculate_box_size(realm, target, Bindings::ResizeObserverBoxOptions::ContentBox);
resize_observer_entry->m_content_box_size.append(content_box_size);
// 5. Set this.devicePixelContentBoxSize slot to result of calculating box size given target and observedBox of "device-pixel-content-box".
auto device_pixel_content_box_size = ResizeObserverSize::calculate_box_size(realm, target, Bindings::ResizeObserverBoxOptions::DevicePixelContentBox);
resize_observer_entry->m_device_pixel_content_box_size.append(device_pixel_content_box_size);
// 6. Set this.contentRect to logical this.contentBoxSize given target and observedBox of "content-box".
double x = 0;
double y = 0;
double width = content_box_size->inline_size();
double height = content_box_size->block_size();
// 7. If target is not an SVG element or target is an SVG element with an associated CSS layout box do these steps:
if (!target.is_svg_element() && target.paintable_box()) {
auto const& paintable_box = *target.paintable_box();
auto absolute_padding_rect = paintable_box.absolute_padding_box_rect();
// Set this.contentRect.top to target.padding top.
y = absolute_padding_rect.y().to_double();
// Set this.contentRect.left to target.padding left.
x = absolute_padding_rect.x().to_double();
} else if (target.is_svg_element() && target.paintable_box()) {
// 8. If target is an SVG element without an associated CSS layout box do these steps:
// Set this.contentRect.top and this.contentRect.left to 0.
// NOTE: This is already done by the default constructor.
}
resize_observer_entry->m_content_rect = MUST(Geometry::DOMRectReadOnly::construct_impl(realm, x, y, width, height));
return resize_observer_entry;
}
void ResizeObserverEntry::initialize(JS::Realm& realm)
{
Base::initialize(realm);
set_prototype(&Bindings::ensure_web_prototype<Bindings::ResizeObserverEntryPrototype>(realm, "ResizeObserverEntry"_fly_string));
}
void ResizeObserverEntry::visit_edges(JS::Cell::Visitor& visitor)
{
Base::visit_edges(visitor);
visitor.visit(m_target);
for (auto& size : m_content_box_size)
visitor.visit(size);
for (auto& size : m_border_box_size)
visitor.visit(size);
}
static JS::NonnullGCPtr<JS::Object> to_js_array(JS::Realm& realm, Vector<JS::NonnullGCPtr<ResizeObserverSize>> const& sizes)
{
Vector<JS::Value> vector;
for (auto const& size : sizes)
vector.append(JS::Value(size.ptr()));
auto array = JS::Array::create_from(realm, vector);
MUST(array->set_integrity_level(JS::Object::IntegrityLevel::Frozen));
return array;
}
JS::NonnullGCPtr<JS::Object> ResizeObserverEntry::border_box_size_js_array() const
{
return to_js_array(realm(), m_border_box_size);
}
JS::NonnullGCPtr<JS::Object> ResizeObserverEntry::content_box_size_js_array() const
{
return to_js_array(realm(), m_content_box_size);
}
JS::NonnullGCPtr<JS::Object> ResizeObserverEntry::device_pixel_content_box_size_js_array() const
{
return to_js_array(realm(), m_device_pixel_content_box_size);
}
}

View File

@ -0,0 +1,56 @@
/*
* Copyright (c) 2024, Aliaksandr Kalenik <kalenik.aliaksandr@gmail.com>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#pragma once
#include <LibJS/Runtime/Array.h>
#include <LibWeb/Bindings/ExceptionOrUtils.h>
#include <LibWeb/Bindings/PlatformObject.h>
#include <LibWeb/Bindings/ResizeObserverPrototype.h>
#include <LibWeb/Geometry/DOMRectReadOnly.h>
#include <LibWeb/ResizeObserver/ResizeObserverSize.h>
namespace Web::ResizeObserver {
// https://drafts.csswg.org/resize-observer-1/#resize-observer-entry-interface
class ResizeObserverEntry : public Bindings::PlatformObject {
WEB_PLATFORM_OBJECT(ResizeObserverEntry, Bindings::PlatformObject);
JS_DECLARE_ALLOCATOR(ResizeObserverEntry);
public:
static WebIDL::ExceptionOr<JS::NonnullGCPtr<ResizeObserverEntry>> create_and_populate(JS::Realm&, DOM::Element& target);
JS::NonnullGCPtr<Geometry::DOMRectReadOnly> content_rect() const { return *m_content_rect; }
JS::NonnullGCPtr<DOM::Element> target() const { return m_target; }
Vector<JS::NonnullGCPtr<ResizeObserverSize>> const& border_box_size() const { return m_border_box_size; }
Vector<JS::NonnullGCPtr<ResizeObserverSize>> const& content_box_size() const { return m_content_box_size; }
Vector<JS::NonnullGCPtr<ResizeObserverSize>> const& device_pixel_content_box_size() const { return m_device_pixel_content_box_size; }
JS::NonnullGCPtr<JS::Object> border_box_size_js_array() const;
JS::NonnullGCPtr<JS::Object> content_box_size_js_array() const;
JS::NonnullGCPtr<JS::Object> device_pixel_content_box_size_js_array() const;
private:
explicit ResizeObserverEntry(JS::Realm& realm, DOM::Element& target)
: PlatformObject(realm)
, m_target(target)
{
}
virtual void initialize(JS::Realm&) override;
virtual void visit_edges(JS::Cell::Visitor&) override;
JS::NonnullGCPtr<DOM::Element> m_target;
Vector<JS::NonnullGCPtr<ResizeObserverSize>> m_content_box_size;
Vector<JS::NonnullGCPtr<ResizeObserverSize>> m_border_box_size;
Vector<JS::NonnullGCPtr<ResizeObserverSize>> m_device_pixel_content_box_size;
JS::GCPtr<Geometry::DOMRectReadOnly> m_content_rect;
};
}

View File

@ -0,0 +1,15 @@
#import <Geometry/DOMRectReadOnly.idl>
#import <DOM/Element.idl>
// https://drafts.csswg.org/resize-observer-1/#resize-observer-entry-interface
[Exposed=Window]
interface ResizeObserverEntry {
readonly attribute Element target;
readonly attribute DOMRectReadOnly contentRect;
// FIXME: Return FrozenArray<ResizeObserverSize> instead of any.
[ImplementedAs=border_box_size_js_array] readonly attribute any borderBoxSize;
// FIXME: Return FrozenArray<ResizeObserverSize> instead of any.
[ImplementedAs=content_box_size_js_array] readonly attribute any contentBoxSize;
// FIXME: Return FrozenArray<ResizeObserverSize> instead of any.
[ImplementedAs=device_pixel_content_box_size_js_array] readonly attribute any devicePixelContentBoxSize;
};

View File

@ -0,0 +1,68 @@
/*
* Copyright (c) 2024, Aliaksandr Kalenik <kalenik.aliaksandr@gmail.com>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#include <LibJS/Heap/Heap.h>
#include <LibWeb/Bindings/ResizeObserverSizePrototype.h>
#include <LibWeb/HTML/Window.h>
#include <LibWeb/Painting/PaintableBox.h>
#include <LibWeb/ResizeObserver/ResizeObserverSize.h>
namespace Web::ResizeObserver {
JS_DEFINE_ALLOCATOR(ResizeObserverSize);
void ResizeObserverSize::initialize(JS::Realm& realm)
{
Base::initialize(realm);
set_prototype(&Bindings::ensure_web_prototype<Bindings::ResizeObserverSizePrototype>(realm, "ResizeObserverSize"_fly_string));
}
// https://drafts.csswg.org/resize-observer-1/#calculate-box-size
JS::NonnullGCPtr<ResizeObserverSize> ResizeObserverSize::calculate_box_size(JS::Realm& realm, DOM::Element& target, Bindings::ResizeObserverBoxOptions observed_box)
{
// 1. Let computedSize be a new ResizeObserverSize object.
auto computed_size = realm.heap().allocate<ResizeObserverSize>(realm, realm);
// FIXME: 2. If target is an SVGGraphicsElement that does not have an associated CSS layout box:
// Otherwise:
if (target.paintable_box()) {
auto const& paintable_box = *target.paintable_box();
switch (observed_box) {
case Bindings::ResizeObserverBoxOptions::BorderBox:
// 1. Set computedSizes inlineSize attribute to targets border area inline length.
computed_size->set_inline_size(paintable_box.border_box_width().to_double());
// 2. Set computedSizes blockSize attribute to targets border area block length.
computed_size->set_block_size(paintable_box.border_box_height().to_double());
break;
case Bindings::ResizeObserverBoxOptions::ContentBox:
// 1. Set computedSizes inlineSize attribute to targets content area inline length.
computed_size->set_inline_size(paintable_box.content_width().to_double());
// 2. Set computedSizes blockSize attribute to targets content area block length.
computed_size->set_block_size(paintable_box.content_height().to_double());
break;
case Bindings::ResizeObserverBoxOptions::DevicePixelContentBox: {
auto device_pixel_ratio = target.document().window().device_pixel_ratio();
// 1. Set computedSizes inlineSize attribute to targets content area inline length, in integral device pixels.
computed_size->set_inline_size(paintable_box.border_box_width().to_double() * device_pixel_ratio);
// 2. Set computedSizes blockSize attribute to targets content area block length, in integral device pixels.
computed_size->set_block_size(paintable_box.border_box_height().to_double() * device_pixel_ratio);
break;
}
default:
VERIFY_NOT_REACHED();
}
}
// 3. Return computedSize.s
return computed_size;
}
bool ResizeObserverSize::equals(ResizeObserverSize const& other) const
{
return m_inline_size == other.m_inline_size && m_block_size == other.m_block_size;
}
}

View File

@ -0,0 +1,42 @@
/*
* Copyright (c) 2024, Aliaksandr Kalenik <kalenik.aliaksandr@gmail.com>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#pragma once
#include <LibWeb/Bindings/PlatformObject.h>
#include <LibWeb/Bindings/ResizeObserverPrototype.h>
namespace Web::ResizeObserver {
// https://drafts.csswg.org/resize-observer-1/#resizeobserversize
class ResizeObserverSize : public Bindings::PlatformObject {
WEB_PLATFORM_OBJECT(ResizeObserverSize, Bindings::PlatformObject);
JS_DECLARE_ALLOCATOR(ResizeObserverSize);
public:
static JS::NonnullGCPtr<ResizeObserverSize> calculate_box_size(JS::Realm& realm, DOM::Element& target, Bindings::ResizeObserverBoxOptions observed_box);
double inline_size() const { return m_inline_size; }
void set_inline_size(double inline_size) { m_inline_size = inline_size; }
double block_size() const { return m_block_size; }
void set_block_size(double block_size) { m_block_size = block_size; }
bool equals(ResizeObserverSize const& other) const;
private:
explicit ResizeObserverSize(JS::Realm& realm)
: PlatformObject(realm)
{
}
virtual void initialize(JS::Realm&) override;
double m_inline_size { 0 };
double m_block_size { 0 };
};
}

View File

@ -0,0 +1,6 @@
// https://drafts.csswg.org/resize-observer-1/#resizeobserversize
[Exposed=Window]
interface ResizeObserverSize {
readonly attribute unrestricted double inlineSize;
readonly attribute unrestricted double blockSize;
};

View File

@ -216,6 +216,8 @@ libweb_js_bindings(PerformanceTimeline/PerformanceObserver)
libweb_js_bindings(PerformanceTimeline/PerformanceObserverEntryList)
libweb_js_bindings(RequestIdleCallback/IdleDeadline)
libweb_js_bindings(ResizeObserver/ResizeObserver)
libweb_js_bindings(ResizeObserver/ResizeObserverEntry)
libweb_js_bindings(ResizeObserver/ResizeObserverSize)
libweb_js_bindings(Streams/ByteLengthQueuingStrategy)
libweb_js_bindings(Streams/CountQueuingStrategy)
libweb_js_bindings(Streams/ReadableByteStreamController)