mirror of
https://github.com/LadybirdBrowser/ladybird.git
synced 2024-12-26 12:41:59 +03:00
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:
parent
fb4c632309
commit
fb8edcea00
Notes:
sideshowbarker
2024-07-17 07:14:09 +09:00
Author: https://github.com/kalenikaliaksandr Commit: https://github.com/SerenityOS/serenity/commit/fb8edcea00 Pull-request: https://github.com/SerenityOS/serenity/pull/23260 Issue: https://github.com/SerenityOS/serenity/issues/23197
@ -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
|
||||
|
@ -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()
|
||||
{
|
||||
|
@ -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 };
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
41
Userland/Libraries/LibWeb/ResizeObserver/ResizeObservation.h
Normal file
41
Userland/Libraries/LibWeb/ResizeObserver/ResizeObservation.h
Normal 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;
|
||||
};
|
||||
|
||||
}
|
@ -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() == ⌖ });
|
||||
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);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -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;
|
||||
};
|
||||
|
||||
}
|
||||
|
@ -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);
|
||||
|
102
Userland/Libraries/LibWeb/ResizeObserver/ResizeObserverEntry.cpp
Normal file
102
Userland/Libraries/LibWeb/ResizeObserver/ResizeObserverEntry.cpp
Normal 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);
|
||||
}
|
||||
|
||||
}
|
@ -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;
|
||||
};
|
||||
|
||||
}
|
@ -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;
|
||||
};
|
@ -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 computedSize’s inlineSize attribute to target’s border area inline length.
|
||||
computed_size->set_inline_size(paintable_box.border_box_width().to_double());
|
||||
// 2. Set computedSize’s blockSize attribute to target’s border area block length.
|
||||
computed_size->set_block_size(paintable_box.border_box_height().to_double());
|
||||
break;
|
||||
case Bindings::ResizeObserverBoxOptions::ContentBox:
|
||||
// 1. Set computedSize’s inlineSize attribute to target’s content area inline length.
|
||||
computed_size->set_inline_size(paintable_box.content_width().to_double());
|
||||
// 2. Set computedSize’s blockSize attribute to target’s 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 computedSize’s inlineSize attribute to target’s 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 computedSize’s blockSize attribute to target’s 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;
|
||||
}
|
||||
|
||||
}
|
@ -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 };
|
||||
};
|
||||
|
||||
}
|
@ -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;
|
||||
};
|
@ -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)
|
||||
|
Loading…
Reference in New Issue
Block a user