mirror of
https://github.com/RGBCube/serenity
synced 2025-07-27 03:37:45 +00:00
LibWeb: Add initial implementation of IntersectionObserver
The main missing features are rootMargin, proper nested browsing context support and content clip/clip-path support. This makes images appear on some sites, such as YouTube and howstuffworks.com.
This commit is contained in:
parent
6f8afd8cd9
commit
165abafb80
16 changed files with 664 additions and 22 deletions
|
@ -4,28 +4,61 @@
|
|||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#include <AK/QuickSort.h>
|
||||
#include <LibWeb/Bindings/Intrinsics.h>
|
||||
#include <LibWeb/DOM/Document.h>
|
||||
#include <LibWeb/DOM/Element.h>
|
||||
#include <LibWeb/IntersectionObserver/IntersectionObserver.h>
|
||||
|
||||
namespace Web::IntersectionObserver {
|
||||
|
||||
// https://w3c.github.io/IntersectionObserver/#dom-intersectionobserver-intersectionobserver
|
||||
WebIDL::ExceptionOr<JS::NonnullGCPtr<IntersectionObserver>> IntersectionObserver::construct_impl(JS::Realm& realm, WebIDL::CallbackType* callback, IntersectionObserverInit const& options)
|
||||
WebIDL::ExceptionOr<JS::NonnullGCPtr<IntersectionObserver>> IntersectionObserver::construct_impl(JS::Realm& realm, JS::GCPtr<WebIDL::CallbackType> callback, IntersectionObserverInit const& options)
|
||||
{
|
||||
// FIXME: Implement
|
||||
(void)callback;
|
||||
(void)options;
|
||||
// 4. Let thresholds be a list equal to options.threshold.
|
||||
Vector<double> thresholds;
|
||||
if (options.threshold.has<double>()) {
|
||||
thresholds.append(options.threshold.get<double>());
|
||||
} else {
|
||||
VERIFY(options.threshold.has<Vector<double>>());
|
||||
thresholds = options.threshold.get<Vector<double>>();
|
||||
}
|
||||
|
||||
return MUST_OR_THROW_OOM(realm.heap().allocate<IntersectionObserver>(realm, realm));
|
||||
// 5. If any value in thresholds is less than 0.0 or greater than 1.0, throw a RangeError exception.
|
||||
for (auto value : thresholds) {
|
||||
if (value < 0.0 || value > 1.0)
|
||||
return WebIDL::SimpleException { WebIDL::SimpleExceptionType::RangeError, "Threshold values must be between 0.0 and 1.0 inclusive"sv };
|
||||
}
|
||||
|
||||
// 6. Sort thresholds in ascending order.
|
||||
quick_sort(thresholds, [](double left, double right) {
|
||||
return left < right;
|
||||
});
|
||||
|
||||
// 1. Let this be a new IntersectionObserver object
|
||||
// 2. Set this’s internal [[callback]] slot to callback.
|
||||
// 8. The thresholds attribute getter will return this sorted thresholds list.
|
||||
// 9. Return this.
|
||||
return MUST_OR_THROW_OOM(realm.heap().allocate<IntersectionObserver>(realm, realm, callback, options.root, move(thresholds)));
|
||||
}
|
||||
|
||||
IntersectionObserver::IntersectionObserver(JS::Realm& realm)
|
||||
IntersectionObserver::IntersectionObserver(JS::Realm& realm, JS::GCPtr<WebIDL::CallbackType> callback, Optional<Variant<JS::Handle<DOM::Element>, JS::Handle<DOM::Document>>> const& root, Vector<double>&& thresholds)
|
||||
: PlatformObject(realm)
|
||||
, m_callback(callback)
|
||||
, m_root(root)
|
||||
, m_thresholds(move(thresholds))
|
||||
{
|
||||
intersection_root().visit([this](auto& node) {
|
||||
node->document().register_intersection_observer({}, *this);
|
||||
});
|
||||
}
|
||||
|
||||
IntersectionObserver::~IntersectionObserver() = default;
|
||||
IntersectionObserver::~IntersectionObserver()
|
||||
{
|
||||
intersection_root().visit([this](auto& node) {
|
||||
node->document().unregister_intersection_observer({}, *this);
|
||||
});
|
||||
}
|
||||
|
||||
JS::ThrowCompletionOr<void> IntersectionObserver::initialize(JS::Realm& realm)
|
||||
{
|
||||
|
@ -35,24 +68,139 @@ JS::ThrowCompletionOr<void> IntersectionObserver::initialize(JS::Realm& realm)
|
|||
return {};
|
||||
}
|
||||
|
||||
void IntersectionObserver::visit_edges(JS::Cell::Visitor& visitor)
|
||||
{
|
||||
Base::visit_edges(visitor);
|
||||
visitor.visit(m_callback);
|
||||
for (auto& entry : m_queued_entries)
|
||||
visitor.visit(entry);
|
||||
for (auto& target : m_observation_targets)
|
||||
visitor.visit(target);
|
||||
}
|
||||
|
||||
// https://w3c.github.io/IntersectionObserver/#dom-intersectionobserver-observe
|
||||
void IntersectionObserver::observe(DOM::Element& target)
|
||||
{
|
||||
// FIXME: Implement
|
||||
(void)target;
|
||||
// Run the observe a target Element algorithm, providing this and target.
|
||||
// https://www.w3.org/TR/intersection-observer/#observe-a-target-element
|
||||
// 1. If target is in observer’s internal [[ObservationTargets]] slot, return.
|
||||
if (m_observation_targets.contains_slow(JS::NonnullGCPtr { target }))
|
||||
return;
|
||||
|
||||
// 2. Let intersectionObserverRegistration be an IntersectionObserverRegistration record with an observer
|
||||
// property set to observer, a previousThresholdIndex property set to -1, and a previousIsIntersecting
|
||||
// property set to false.
|
||||
auto intersection_observer_registration = IntersectionObserverRegistration {
|
||||
.observer = JS::make_handle(*this),
|
||||
.previous_threshold_index = OptionalNone {},
|
||||
.previous_is_intersecting = false,
|
||||
};
|
||||
|
||||
// 3. Append intersectionObserverRegistration to target’s internal [[RegisteredIntersectionObservers]] slot.
|
||||
target.register_intersection_observer({}, move(intersection_observer_registration));
|
||||
|
||||
// 4. Add target to observer’s internal [[ObservationTargets]] slot.
|
||||
m_observation_targets.append(target);
|
||||
}
|
||||
|
||||
// https://w3c.github.io/IntersectionObserver/#dom-intersectionobserver-unobserve
|
||||
void IntersectionObserver::unobserve(DOM::Element& target)
|
||||
{
|
||||
// FIXME: Implement
|
||||
(void)target;
|
||||
// Run the unobserve a target Element algorithm, providing this and target.
|
||||
// https://www.w3.org/TR/intersection-observer/#unobserve-a-target-element
|
||||
// 1. Remove the IntersectionObserverRegistration record whose observer property is equal to this from target’s internal [[RegisteredIntersectionObservers]] slot, if present.
|
||||
target.unregister_intersection_observer({}, *this);
|
||||
|
||||
// 2. Remove target from this’s internal [[ObservationTargets]] slot, if present
|
||||
m_observation_targets.remove_first_matching([&target](JS::NonnullGCPtr<DOM::Element> const& entry) {
|
||||
return entry.ptr() == ⌖
|
||||
});
|
||||
}
|
||||
|
||||
// https://w3c.github.io/IntersectionObserver/#dom-intersectionobserver-disconnect
|
||||
void IntersectionObserver::disconnect()
|
||||
{
|
||||
// FIXME: Implement
|
||||
// For each target in this’s internal [[ObservationTargets]] slot:
|
||||
// 1. Remove the IntersectionObserverRegistration record whose observer property is equal to this from target’s internal
|
||||
// [[RegisteredIntersectionObservers]] slot.
|
||||
// 2. Remove target from this’s internal [[ObservationTargets]] slot.
|
||||
for (auto& target : m_observation_targets) {
|
||||
target->unregister_intersection_observer({}, *this);
|
||||
}
|
||||
m_observation_targets.clear();
|
||||
}
|
||||
|
||||
// https://www.w3.org/TR/intersection-observer/#dom-intersectionobserver-takerecords
|
||||
Vector<JS::Handle<IntersectionObserverEntry>> IntersectionObserver::take_records()
|
||||
{
|
||||
// 1. Let queue be a copy of this’s internal [[QueuedEntries]] slot.
|
||||
Vector<JS::Handle<IntersectionObserverEntry>> queue;
|
||||
for (auto& entry : m_queued_entries)
|
||||
queue.append(*entry);
|
||||
|
||||
// 2. Clear this’s internal [[QueuedEntries]] slot.
|
||||
m_queued_entries.clear();
|
||||
|
||||
// 3. Return queue.
|
||||
return queue;
|
||||
}
|
||||
|
||||
Variant<JS::Handle<DOM::Element>, JS::Handle<DOM::Document>, Empty> IntersectionObserver::root() const
|
||||
{
|
||||
if (!m_root.has_value())
|
||||
return Empty {};
|
||||
return m_root.value();
|
||||
}
|
||||
|
||||
Variant<JS::Handle<DOM::Element>, JS::Handle<DOM::Document>> IntersectionObserver::intersection_root() const
|
||||
{
|
||||
if (!m_root.has_value())
|
||||
return JS::make_handle(global_object().browsing_context()->top_level_browsing_context().active_document());
|
||||
return m_root.value();
|
||||
}
|
||||
|
||||
// https://www.w3.org/TR/intersection-observer/#intersectionobserver-root-intersection-rectangle
|
||||
CSSPixelRect IntersectionObserver::root_intersection_rectangle() const
|
||||
{
|
||||
// If the IntersectionObserver is an implicit root observer,
|
||||
// it’s treated as if the root were the top-level browsing context’s document, according to the following rule for document.
|
||||
auto intersection_root = this->intersection_root();
|
||||
|
||||
CSSPixelRect rect;
|
||||
|
||||
// If the intersection root is a document,
|
||||
// it’s the size of the document's viewport (note that this processing step can only be reached if the document is fully active).
|
||||
if (intersection_root.has<JS::Handle<DOM::Document>>()) {
|
||||
auto document = intersection_root.get<JS::Handle<DOM::Document>>();
|
||||
|
||||
// Since the spec says that this is only reach if the document is fully active, that means it must have a browsing context.
|
||||
VERIFY(document->browsing_context());
|
||||
rect = document->browsing_context()->viewport_rect();
|
||||
} else {
|
||||
VERIFY(intersection_root.has<JS::Handle<DOM::Element>>());
|
||||
auto element = intersection_root.get<JS::Handle<DOM::Element>>();
|
||||
|
||||
// FIXME: Otherwise, if the intersection root has a content clip,
|
||||
// it’s the element’s content area.
|
||||
|
||||
// Otherwise,
|
||||
// it’s the result of getting the bounding box for the intersection root.
|
||||
auto bounding_client_rect = element->get_bounding_client_rect();
|
||||
rect = CSSPixelRect(bounding_client_rect->x(), bounding_client_rect->y(), bounding_client_rect->width(), bounding_client_rect->height());
|
||||
}
|
||||
|
||||
// FIXME: When calculating the root intersection rectangle for a same-origin-domain target, the rectangle is then
|
||||
// expanded according to the offsets in the IntersectionObserver’s [[rootMargin]] slot in a manner similar
|
||||
// to CSS’s margin property, with the four values indicating the amount the top, right, bottom, and left
|
||||
// edges, respectively, are offset by, with positive lengths indicating an outward offset. Percentages
|
||||
// are resolved relative to the width of the undilated rectangle.
|
||||
|
||||
return rect;
|
||||
}
|
||||
|
||||
void IntersectionObserver::queue_entry(Badge<DOM::Document>, JS::NonnullGCPtr<IntersectionObserverEntry> entry)
|
||||
{
|
||||
m_queued_entries.append(entry);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -8,32 +8,79 @@
|
|||
|
||||
#include <LibJS/Heap/Handle.h>
|
||||
#include <LibWeb/Bindings/PlatformObject.h>
|
||||
#include <LibWeb/IntersectionObserver/IntersectionObserverEntry.h>
|
||||
#include <LibWeb/PixelUnits.h>
|
||||
|
||||
namespace Web::IntersectionObserver {
|
||||
|
||||
struct IntersectionObserverInit {
|
||||
Optional<Variant<JS::Handle<DOM::Element>, JS::Handle<DOM::Document>>> root;
|
||||
DeprecatedString root_margin { "0px"sv };
|
||||
String root_margin { "0px"_short_string };
|
||||
Variant<double, Vector<double>> threshold { 0 };
|
||||
};
|
||||
|
||||
// https://www.w3.org/TR/intersection-observer/#intersectionobserverregistration
|
||||
struct IntersectionObserverRegistration {
|
||||
// https://www.w3.org/TR/intersection-observer/#dom-intersectionobserverregistration-observer
|
||||
// [A]n observer property holding an IntersectionObserver.
|
||||
JS::Handle<IntersectionObserver> observer;
|
||||
|
||||
// https://www.w3.org/TR/intersection-observer/#dom-intersectionobserverregistration-observer
|
||||
// NOTE: Optional is used in place of the spec using -1 to indicate no previous index.
|
||||
// [A] previousThresholdIndex property holding a number between -1 and the length of the observer’s thresholds property (inclusive).
|
||||
Optional<size_t> previous_threshold_index;
|
||||
|
||||
// https://www.w3.org/TR/intersection-observer/#dom-intersectionobserverregistration-previousisintersecting
|
||||
// [A] previousIsIntersecting property holding a boolean.
|
||||
bool previous_is_intersecting { false };
|
||||
};
|
||||
|
||||
// https://w3c.github.io/IntersectionObserver/#intersection-observer-interface
|
||||
class IntersectionObserver : public Bindings::PlatformObject {
|
||||
class IntersectionObserver final : public Bindings::PlatformObject {
|
||||
WEB_PLATFORM_OBJECT(IntersectionObserver, Bindings::PlatformObject);
|
||||
|
||||
public:
|
||||
static WebIDL::ExceptionOr<JS::NonnullGCPtr<IntersectionObserver>> construct_impl(JS::Realm&, WebIDL::CallbackType* callback, IntersectionObserverInit const& options = {});
|
||||
static WebIDL::ExceptionOr<JS::NonnullGCPtr<IntersectionObserver>> construct_impl(JS::Realm&, JS::GCPtr<WebIDL::CallbackType> callback, IntersectionObserverInit const& options = {});
|
||||
|
||||
virtual ~IntersectionObserver() override;
|
||||
|
||||
void observe(DOM::Element& target);
|
||||
void unobserve(DOM::Element& target);
|
||||
void disconnect();
|
||||
Vector<JS::Handle<IntersectionObserverEntry>> take_records();
|
||||
|
||||
Vector<JS::NonnullGCPtr<DOM::Element>> const& observation_targets() const { return m_observation_targets; }
|
||||
|
||||
Variant<JS::Handle<DOM::Element>, JS::Handle<DOM::Document>, Empty> root() const;
|
||||
Vector<double> const& thresholds() const { return m_thresholds; }
|
||||
|
||||
Variant<JS::Handle<DOM::Element>, JS::Handle<DOM::Document>> intersection_root() const;
|
||||
CSSPixelRect root_intersection_rectangle() const;
|
||||
|
||||
void queue_entry(Badge<DOM::Document>, JS::NonnullGCPtr<IntersectionObserverEntry>);
|
||||
|
||||
WebIDL::CallbackType& callback() { return *m_callback; }
|
||||
|
||||
private:
|
||||
explicit IntersectionObserver(JS::Realm&);
|
||||
explicit IntersectionObserver(JS::Realm&, JS::GCPtr<WebIDL::CallbackType> callback, Optional<Variant<JS::Handle<DOM::Element>, JS::Handle<DOM::Document>>> const& root, Vector<double>&& thresholds);
|
||||
|
||||
virtual JS::ThrowCompletionOr<void> initialize(JS::Realm&) override;
|
||||
virtual void visit_edges(JS::Cell::Visitor&) override;
|
||||
|
||||
// https://www.w3.org/TR/intersection-observer/#dom-intersectionobserver-callback-slot
|
||||
JS::GCPtr<WebIDL::CallbackType> m_callback;
|
||||
|
||||
// https://www.w3.org/TR/intersection-observer/#dom-intersectionobserver-root
|
||||
Optional<Variant<JS::Handle<DOM::Element>, JS::Handle<DOM::Document>>> m_root;
|
||||
|
||||
// https://www.w3.org/TR/intersection-observer/#dom-intersectionobserver-thresholds
|
||||
Vector<double> m_thresholds;
|
||||
|
||||
// https://www.w3.org/TR/intersection-observer/#dom-intersectionobserver-queuedentries-slot
|
||||
Vector<JS::NonnullGCPtr<IntersectionObserverEntry>> m_queued_entries;
|
||||
|
||||
// https://www.w3.org/TR/intersection-observer/#dom-intersectionobserver-observationtargets-slot
|
||||
Vector<JS::NonnullGCPtr<DOM::Element>> m_observation_targets;
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
@ -1,19 +1,21 @@
|
|||
#import <DOM/Document.idl>
|
||||
#import <DOM/Element.idl>
|
||||
#import <DOM/Node.idl>
|
||||
#import <IntersectionObserver/IntersectionObserverEntry.idl>
|
||||
|
||||
callback IntersectionObserverCallback = undefined (sequence<IntersectionObserverEntry> entries, IntersectionObserver observer);
|
||||
|
||||
[Exposed=(Window)]
|
||||
[Exposed=(Window), UseNewAKString]
|
||||
interface IntersectionObserver {
|
||||
constructor(IntersectionObserverCallback callback, optional IntersectionObserverInit options = {});
|
||||
|
||||
readonly attribute (Element or Document)? root;
|
||||
// FIXME: readonly attribute DOMString rootMargin;
|
||||
// FIXME: `sequence<double>` should be `FrozenArray<double>`
|
||||
readonly attribute sequence<double> thresholds;
|
||||
undefined observe(Element target);
|
||||
undefined unobserve(Element target);
|
||||
undefined disconnect();
|
||||
|
||||
// FIXME:
|
||||
// sequence<IntersectionObserverEntry> takeRecords();
|
||||
sequence<IntersectionObserverEntry> takeRecords();
|
||||
};
|
||||
|
||||
dictionary IntersectionObserverInit {
|
||||
|
|
|
@ -0,0 +1,57 @@
|
|||
/*
|
||||
* Copyright (c) 2023, Luke Wilde <lukew@serenityos.org>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#include <LibWeb/Bindings/Intrinsics.h>
|
||||
#include <LibWeb/DOM/Element.h>
|
||||
#include <LibWeb/IntersectionObserver/IntersectionObserverEntry.h>
|
||||
|
||||
namespace Web::IntersectionObserver {
|
||||
|
||||
WebIDL::ExceptionOr<JS::NonnullGCPtr<IntersectionObserverEntry>> IntersectionObserverEntry::construct_impl(JS::Realm& realm, Web::IntersectionObserver::IntersectionObserverEntryInit const& options)
|
||||
{
|
||||
auto& vm = realm.vm();
|
||||
|
||||
JS::GCPtr<Geometry::DOMRectReadOnly> root_bounds;
|
||||
if (options.root_bounds.has_value())
|
||||
root_bounds = TRY(Geometry::DOMRectReadOnly::from_rect(vm, options.root_bounds.value()));
|
||||
|
||||
auto bounding_client_rect = TRY(Geometry::DOMRectReadOnly::from_rect(vm, options.bounding_client_rect));
|
||||
auto intersection_rect = TRY(Geometry::DOMRectReadOnly::from_rect(vm, options.intersection_rect));
|
||||
return MUST_OR_THROW_OOM(realm.heap().allocate<IntersectionObserverEntry>(realm, realm, options.time, root_bounds, bounding_client_rect, intersection_rect, options.is_intersecting, options.intersection_ratio, *options.target));
|
||||
}
|
||||
|
||||
IntersectionObserverEntry::IntersectionObserverEntry(JS::Realm& realm, HighResolutionTime::DOMHighResTimeStamp time, JS::GCPtr<Geometry::DOMRectReadOnly> root_bounds, JS::NonnullGCPtr<Geometry::DOMRectReadOnly> bounding_client_rect, JS::NonnullGCPtr<Geometry::DOMRectReadOnly> intersection_rect, bool is_intersecting, double intersection_ratio, JS::NonnullGCPtr<DOM::Element> target)
|
||||
: Bindings::PlatformObject(realm)
|
||||
, m_time(time)
|
||||
, m_root_bounds(root_bounds)
|
||||
, m_bounding_client_rect(bounding_client_rect)
|
||||
, m_intersection_rect(intersection_rect)
|
||||
, m_is_intersecting(is_intersecting)
|
||||
, m_intersection_ratio(intersection_ratio)
|
||||
, m_target(target)
|
||||
{
|
||||
}
|
||||
|
||||
IntersectionObserverEntry::~IntersectionObserverEntry() = default;
|
||||
|
||||
JS::ThrowCompletionOr<void> IntersectionObserverEntry::initialize(JS::Realm& realm)
|
||||
{
|
||||
MUST_OR_THROW_OOM(Base::initialize(realm));
|
||||
set_prototype(&Bindings::ensure_web_prototype<Bindings::IntersectionObserverEntryPrototype>(realm, "IntersectionObserverEntry"));
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
void IntersectionObserverEntry::visit_edges(JS::Cell::Visitor& visitor)
|
||||
{
|
||||
Base::visit_edges(visitor);
|
||||
visitor.visit(m_root_bounds);
|
||||
visitor.visit(m_bounding_client_rect);
|
||||
visitor.visit(m_intersection_rect);
|
||||
visitor.visit(m_target);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,82 @@
|
|||
/*
|
||||
* Copyright (c) 2023, Luke Wilde <lukew@serenityos.org>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <LibWeb/Bindings/PlatformObject.h>
|
||||
#include <LibWeb/Geometry/DOMRect.h>
|
||||
#include <LibWeb/HighResolutionTime/DOMHighResTimeStamp.h>
|
||||
|
||||
namespace Web::IntersectionObserver {
|
||||
|
||||
struct IntersectionObserverEntryInit {
|
||||
// https://www.w3.org/TR/intersection-observer/#dom-intersectionobserverentry-time
|
||||
HighResolutionTime::DOMHighResTimeStamp time { 0.0 };
|
||||
|
||||
// https://www.w3.org/TR/intersection-observer/#dom-intersectionobserverentry-rootbounds
|
||||
Optional<Geometry::DOMRectInit> root_bounds;
|
||||
|
||||
// https://www.w3.org/TR/intersection-observer/#dom-intersectionobserverentry-boundingclientrect
|
||||
Geometry::DOMRectInit bounding_client_rect;
|
||||
|
||||
// https://www.w3.org/TR/intersection-observer/#dom-intersectionobserverentry-intersectionrect
|
||||
Geometry::DOMRectInit intersection_rect;
|
||||
|
||||
// https://www.w3.org/TR/intersection-observer/#dom-intersectionobserverentry-isintersecting
|
||||
bool is_intersecting { false };
|
||||
|
||||
// https://www.w3.org/TR/intersection-observer/#dom-intersectionobserverentry-intersectionratio
|
||||
double intersection_ratio { 0.0 };
|
||||
|
||||
// https://www.w3.org/TR/intersection-observer/#dom-intersectionobserverentry-target
|
||||
JS::Handle<DOM::Element> target;
|
||||
};
|
||||
|
||||
class IntersectionObserverEntry final : public Bindings::PlatformObject {
|
||||
WEB_PLATFORM_OBJECT(IntersectionObserverEntry, Bindings::PlatformObject);
|
||||
|
||||
public:
|
||||
static WebIDL::ExceptionOr<JS::NonnullGCPtr<IntersectionObserverEntry>> construct_impl(JS::Realm&, IntersectionObserverEntryInit const& options);
|
||||
|
||||
virtual ~IntersectionObserverEntry() override;
|
||||
|
||||
HighResolutionTime::DOMHighResTimeStamp time() const { return m_time; }
|
||||
JS::GCPtr<Geometry::DOMRectReadOnly> root_bounds() const { return m_root_bounds; }
|
||||
JS::NonnullGCPtr<Geometry::DOMRectReadOnly> bounding_client_rect() const { return m_bounding_client_rect; }
|
||||
JS::NonnullGCPtr<Geometry::DOMRectReadOnly> intersection_rect() const { return m_intersection_rect; }
|
||||
bool is_intersecting() const { return m_is_intersecting; }
|
||||
double intersection_ratio() const { return m_intersection_ratio; }
|
||||
JS::NonnullGCPtr<DOM::Element> target() const { return m_target; }
|
||||
|
||||
private:
|
||||
IntersectionObserverEntry(JS::Realm&, HighResolutionTime::DOMHighResTimeStamp time, JS::GCPtr<Geometry::DOMRectReadOnly> root_bounds, JS::NonnullGCPtr<Geometry::DOMRectReadOnly> bounding_client_rect, JS::NonnullGCPtr<Geometry::DOMRectReadOnly> intersection_rect, bool is_intersecting, double intersection_ratio, JS::NonnullGCPtr<DOM::Element> target);
|
||||
|
||||
virtual JS::ThrowCompletionOr<void> initialize(JS::Realm&) override;
|
||||
virtual void visit_edges(JS::Cell::Visitor&) override;
|
||||
|
||||
// https://www.w3.org/TR/intersection-observer/#dom-intersectionobserverentry-time
|
||||
HighResolutionTime::DOMHighResTimeStamp m_time { 0.0 };
|
||||
|
||||
// https://www.w3.org/TR/intersection-observer/#dom-intersectionobserverentry-rootbounds
|
||||
JS::GCPtr<Geometry::DOMRectReadOnly> m_root_bounds;
|
||||
|
||||
// https://www.w3.org/TR/intersection-observer/#dom-intersectionobserverentry-boundingclientrect
|
||||
JS::NonnullGCPtr<Geometry::DOMRectReadOnly> m_bounding_client_rect;
|
||||
|
||||
// https://www.w3.org/TR/intersection-observer/#dom-intersectionobserverentry-intersectionrect
|
||||
JS::NonnullGCPtr<Geometry::DOMRectReadOnly> m_intersection_rect;
|
||||
|
||||
// https://www.w3.org/TR/intersection-observer/#dom-intersectionobserverentry-isintersecting
|
||||
bool m_is_intersecting { false };
|
||||
|
||||
// https://www.w3.org/TR/intersection-observer/#dom-intersectionobserverentry-intersectionratio
|
||||
double m_intersection_ratio { 0.0 };
|
||||
|
||||
// https://www.w3.org/TR/intersection-observer/#dom-intersectionobserverentry-target
|
||||
JS::NonnullGCPtr<DOM::Element> m_target;
|
||||
};
|
||||
|
||||
}
|
|
@ -0,0 +1,26 @@
|
|||
#import <DOM/Element.idl>
|
||||
#import <Geometry/DOMRect.idl>
|
||||
#import <HighResolutionTime/DOMHighResTimeStamp.idl>
|
||||
|
||||
// https://www.w3.org/TR/intersection-observer/#intersectionobserverentry
|
||||
[Exposed=Window]
|
||||
interface IntersectionObserverEntry {
|
||||
// FIXME: constructor(IntersectionObserverEntryInit intersectionObserverEntryInit);
|
||||
readonly attribute DOMHighResTimeStamp time;
|
||||
readonly attribute DOMRectReadOnly? rootBounds;
|
||||
readonly attribute DOMRectReadOnly boundingClientRect;
|
||||
readonly attribute DOMRectReadOnly intersectionRect;
|
||||
readonly attribute boolean isIntersecting;
|
||||
readonly attribute double intersectionRatio;
|
||||
readonly attribute Element target;
|
||||
};
|
||||
|
||||
dictionary IntersectionObserverEntryInit {
|
||||
required DOMHighResTimeStamp time;
|
||||
required DOMRectInit? rootBounds;
|
||||
required DOMRectInit boundingClientRect;
|
||||
required DOMRectInit intersectionRect;
|
||||
required boolean isIntersecting;
|
||||
required double intersectionRatio;
|
||||
required Element target;
|
||||
};
|
Loading…
Add table
Add a link
Reference in a new issue