mirror of
https://github.com/LadybirdBrowser/ladybird.git
synced 2025-07-28 19:59:17 +00:00
LibWeb: Implement more IntersectionObserver attributes
This commit is contained in:
parent
7444f76b0d
commit
3e536a4cd7
Notes:
github-actions[bot]
2024-11-23 08:53:22 +00:00
Author: https://github.com/Psychpsyo
Commit: 3e536a4cd7
Pull-request: https://github.com/LadybirdBrowser/ladybird/pull/2507
9 changed files with 345 additions and 17 deletions
|
@ -6039,6 +6039,11 @@ Vector<ParsedFontFace::Source> Parser::parse_font_face_src(TokenStream<T>& compo
|
||||||
template Vector<ParsedFontFace::Source> Parser::parse_font_face_src(TokenStream<Token>& component_values);
|
template Vector<ParsedFontFace::Source> Parser::parse_font_face_src(TokenStream<Token>& component_values);
|
||||||
template Vector<ParsedFontFace::Source> Parser::parse_font_face_src(TokenStream<ComponentValue>& component_values);
|
template Vector<ParsedFontFace::Source> Parser::parse_font_face_src(TokenStream<ComponentValue>& component_values);
|
||||||
|
|
||||||
|
Vector<ComponentValue> Parser::parse_as_list_of_component_values()
|
||||||
|
{
|
||||||
|
return parse_a_list_of_component_values(m_token_stream);
|
||||||
|
}
|
||||||
|
|
||||||
RefPtr<CSSStyleValue> Parser::parse_list_style_value(TokenStream<ComponentValue>& tokens)
|
RefPtr<CSSStyleValue> Parser::parse_list_style_value(TokenStream<ComponentValue>& tokens)
|
||||||
{
|
{
|
||||||
RefPtr<CSSStyleValue> list_position;
|
RefPtr<CSSStyleValue> list_position;
|
||||||
|
|
|
@ -69,6 +69,8 @@ public:
|
||||||
|
|
||||||
Vector<ParsedFontFace::Source> parse_as_font_face_src();
|
Vector<ParsedFontFace::Source> parse_as_font_face_src();
|
||||||
|
|
||||||
|
Vector<ComponentValue> parse_as_list_of_component_values();
|
||||||
|
|
||||||
static NonnullRefPtr<CSSStyleValue> resolve_unresolved_style_value(ParsingContext const&, DOM::Element&, Optional<CSS::Selector::PseudoElement::Type>, PropertyID, UnresolvedStyleValue const&);
|
static NonnullRefPtr<CSSStyleValue> resolve_unresolved_style_value(ParsingContext const&, DOM::Element&, Optional<CSS::Selector::PseudoElement::Type>, PropertyID, UnresolvedStyleValue const&);
|
||||||
|
|
||||||
[[nodiscard]] LengthOrCalculated parse_as_sizes_attribute(DOM::Element const& element, HTML::HTMLImageElement const* img = nullptr);
|
[[nodiscard]] LengthOrCalculated parse_as_sizes_attribute(DOM::Element const& element, HTML::HTMLImageElement const* img = nullptr);
|
||||||
|
|
|
@ -7,6 +7,8 @@
|
||||||
#include <AK/QuickSort.h>
|
#include <AK/QuickSort.h>
|
||||||
#include <LibWeb/Bindings/IntersectionObserverPrototype.h>
|
#include <LibWeb/Bindings/IntersectionObserverPrototype.h>
|
||||||
#include <LibWeb/Bindings/Intrinsics.h>
|
#include <LibWeb/Bindings/Intrinsics.h>
|
||||||
|
#include <LibWeb/CSS/Parser/Parser.h>
|
||||||
|
#include <LibWeb/CSS/StyleValues/LengthStyleValue.h>
|
||||||
#include <LibWeb/DOM/Document.h>
|
#include <LibWeb/DOM/Document.h>
|
||||||
#include <LibWeb/DOM/Element.h>
|
#include <LibWeb/DOM/Element.h>
|
||||||
#include <LibWeb/HTML/TraversableNavigable.h>
|
#include <LibWeb/HTML/TraversableNavigable.h>
|
||||||
|
@ -21,7 +23,24 @@ GC_DEFINE_ALLOCATOR(IntersectionObserver);
|
||||||
// https://w3c.github.io/IntersectionObserver/#dom-intersectionobserver-intersectionobserver
|
// https://w3c.github.io/IntersectionObserver/#dom-intersectionobserver-intersectionobserver
|
||||||
WebIDL::ExceptionOr<GC::Ref<IntersectionObserver>> IntersectionObserver::construct_impl(JS::Realm& realm, GC::Ptr<WebIDL::CallbackType> callback, IntersectionObserverInit const& options)
|
WebIDL::ExceptionOr<GC::Ref<IntersectionObserver>> IntersectionObserver::construct_impl(JS::Realm& realm, GC::Ptr<WebIDL::CallbackType> callback, IntersectionObserverInit const& options)
|
||||||
{
|
{
|
||||||
// 4. Let thresholds be a list equal to options.threshold.
|
// https://w3c.github.io/IntersectionObserver/#initialize-a-new-intersectionobserver
|
||||||
|
// 1. Let this be a new IntersectionObserver object
|
||||||
|
// 2. Set this’s internal [[callback]] slot to callback.
|
||||||
|
// NOTE: Steps 1 and 2 are handled by creating the IntersectionObserver at the very end of this function.
|
||||||
|
|
||||||
|
// 3. Attempt to parse a margin from options.rootMargin. If a list is returned, set this’s internal [[rootMargin]] slot to that. Otherwise, throw a SyntaxError exception.
|
||||||
|
auto root_margin = parse_a_margin(realm, options.root_margin);
|
||||||
|
if (!root_margin.has_value()) {
|
||||||
|
return WebIDL::SyntaxError::create(realm, "IntersectionObserver: Cannot parse root margin as a margin."_string);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 4. Attempt to parse a margin from options.scrollMargin. If a list is returned, set this’s internal [[scrollMargin]] slot to that. Otherwise, throw a SyntaxError exception.
|
||||||
|
auto scroll_margin = parse_a_margin(realm, options.scroll_margin);
|
||||||
|
if (!scroll_margin.has_value()) {
|
||||||
|
return WebIDL::SyntaxError::create(realm, "IntersectionObserver: Cannot parse scroll margin as a margin."_string);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 5. Let thresholds be a list equal to options.threshold.
|
||||||
Vector<double> thresholds;
|
Vector<double> thresholds;
|
||||||
if (options.threshold.has<double>()) {
|
if (options.threshold.has<double>()) {
|
||||||
thresholds.append(options.threshold.get<double>());
|
thresholds.append(options.threshold.get<double>());
|
||||||
|
@ -30,28 +49,47 @@ WebIDL::ExceptionOr<GC::Ref<IntersectionObserver>> IntersectionObserver::constru
|
||||||
thresholds = options.threshold.get<Vector<double>>();
|
thresholds = options.threshold.get<Vector<double>>();
|
||||||
}
|
}
|
||||||
|
|
||||||
// 5. If any value in thresholds is less than 0.0 or greater than 1.0, throw a RangeError exception.
|
// 6. If any value in thresholds is less than 0.0 or greater than 1.0, throw a RangeError exception.
|
||||||
for (auto value : thresholds) {
|
for (auto value : thresholds) {
|
||||||
if (value < 0.0 || value > 1.0)
|
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 };
|
return WebIDL::SimpleException { WebIDL::SimpleExceptionType::RangeError, "Threshold values must be between 0.0 and 1.0 inclusive"sv };
|
||||||
}
|
}
|
||||||
|
|
||||||
// 6. Sort thresholds in ascending order.
|
// 7. Sort thresholds in ascending order.
|
||||||
quick_sort(thresholds, [](double left, double right) {
|
quick_sort(thresholds, [](double left, double right) {
|
||||||
return left < right;
|
return left < right;
|
||||||
});
|
});
|
||||||
|
|
||||||
// 1. Let this be a new IntersectionObserver object
|
// 8. If thresholds is empty, append 0 to thresholds.
|
||||||
// 2. Set this’s internal [[callback]] slot to callback.
|
if (thresholds.is_empty()) {
|
||||||
// 8. The thresholds attribute getter will return this sorted thresholds list.
|
thresholds.append(0);
|
||||||
// 9. Return this.
|
}
|
||||||
return realm.create<IntersectionObserver>(realm, callback, options.root, move(thresholds));
|
|
||||||
|
// 9. The thresholds attribute getter will return this sorted thresholds list.
|
||||||
|
// NOTE: Handled implicitly by passing it into the constructor at the end of this function
|
||||||
|
|
||||||
|
// 10. Let delay be the value of options.delay.
|
||||||
|
auto delay = options.delay;
|
||||||
|
|
||||||
|
// 11. If options.trackVisibility is true and delay is less than 100, set delay to 100.
|
||||||
|
if (options.track_visibility && delay < 100) {
|
||||||
|
delay = 100;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 12. Set this’s internal [[delay]] slot to options.delay to delay.
|
||||||
|
// 13. Set this’s internal [[trackVisibility]] slot to options.trackVisibility.
|
||||||
|
// 14. Return this.
|
||||||
|
return realm.create<IntersectionObserver>(realm, callback, options.root, move(root_margin.value()), move(scroll_margin.value()), move(thresholds), move(delay), move(options.track_visibility));
|
||||||
}
|
}
|
||||||
|
|
||||||
IntersectionObserver::IntersectionObserver(JS::Realm& realm, GC::Ptr<WebIDL::CallbackType> callback, Optional<Variant<GC::Root<DOM::Element>, GC::Root<DOM::Document>>> const& root, Vector<double>&& thresholds)
|
IntersectionObserver::IntersectionObserver(JS::Realm& realm, GC::Ptr<WebIDL::CallbackType> callback, Optional<Variant<GC::Root<DOM::Element>, GC::Root<DOM::Document>>> const& root, Vector<CSS::LengthPercentage> root_margin, Vector<CSS::LengthPercentage> scroll_margin, Vector<double>&& thresholds, double delay, bool track_visibility)
|
||||||
: PlatformObject(realm)
|
: PlatformObject(realm)
|
||||||
, m_callback(callback)
|
, m_callback(callback)
|
||||||
|
, m_root_margin(root_margin)
|
||||||
|
, m_scroll_margin(scroll_margin)
|
||||||
, m_thresholds(move(thresholds))
|
, m_thresholds(move(thresholds))
|
||||||
|
, m_delay(delay)
|
||||||
|
, m_track_visibility(track_visibility)
|
||||||
{
|
{
|
||||||
m_root = root.has_value() ? root->visit([](auto& value) -> GC::Ptr<DOM::Node> { return *value; }) : nullptr;
|
m_root = root.has_value() ? root->visit([](auto& value) -> GC::Ptr<DOM::Node> { return *value; }) : nullptr;
|
||||||
intersection_root().visit([this](auto& node) {
|
intersection_root().visit([this](auto& node) {
|
||||||
|
@ -161,6 +199,44 @@ Variant<GC::Root<DOM::Element>, GC::Root<DOM::Document>, Empty> IntersectionObse
|
||||||
VERIFY_NOT_REACHED();
|
VERIFY_NOT_REACHED();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// https://w3c.github.io/IntersectionObserver/#dom-intersectionobserver-rootmargin
|
||||||
|
String IntersectionObserver::root_margin() const
|
||||||
|
{
|
||||||
|
// On getting, return the result of serializing the elements of [[rootMargin]] space-separated, where pixel
|
||||||
|
// lengths serialize as the numeric value followed by "px", and percentages serialize as the numeric value
|
||||||
|
// followed by "%". Note that this is not guaranteed to be identical to the options.rootMargin passed to the
|
||||||
|
// IntersectionObserver constructor. If no rootMargin was passed to the IntersectionObserver
|
||||||
|
// constructor, the value of this attribute is "0px 0px 0px 0px".
|
||||||
|
StringBuilder builder;
|
||||||
|
builder.append(m_root_margin[0].to_string());
|
||||||
|
builder.append(' ');
|
||||||
|
builder.append(m_root_margin[1].to_string());
|
||||||
|
builder.append(' ');
|
||||||
|
builder.append(m_root_margin[2].to_string());
|
||||||
|
builder.append(' ');
|
||||||
|
builder.append(m_root_margin[3].to_string());
|
||||||
|
return builder.to_string().value();
|
||||||
|
}
|
||||||
|
|
||||||
|
// https://w3c.github.io/IntersectionObserver/#dom-intersectionobserver-scrollmargin
|
||||||
|
String IntersectionObserver::scroll_margin() const
|
||||||
|
{
|
||||||
|
// On getting, return the result of serializing the elements of [[scrollMargin]] space-separated, where pixel
|
||||||
|
// lengths serialize as the numeric value followed by "px", and percentages serialize as the numeric value
|
||||||
|
// followed by "%". Note that this is not guaranteed to be identical to the options.scrollMargin passed to the
|
||||||
|
// IntersectionObserver constructor. If no scrollMargin was passed to the IntersectionObserver
|
||||||
|
// constructor, the value of this attribute is "0px 0px 0px 0px".
|
||||||
|
StringBuilder builder;
|
||||||
|
builder.append(m_scroll_margin[0].to_string());
|
||||||
|
builder.append(' ');
|
||||||
|
builder.append(m_scroll_margin[1].to_string());
|
||||||
|
builder.append(' ');
|
||||||
|
builder.append(m_scroll_margin[2].to_string());
|
||||||
|
builder.append(' ');
|
||||||
|
builder.append(m_scroll_margin[3].to_string());
|
||||||
|
return builder.to_string().value();
|
||||||
|
}
|
||||||
|
|
||||||
// https://www.w3.org/TR/intersection-observer/#intersectionobserver-intersection-root
|
// https://www.w3.org/TR/intersection-observer/#intersectionobserver-intersection-root
|
||||||
Variant<GC::Root<DOM::Element>, GC::Root<DOM::Document>> IntersectionObserver::intersection_root() const
|
Variant<GC::Root<DOM::Element>, GC::Root<DOM::Document>> IntersectionObserver::intersection_root() const
|
||||||
{
|
{
|
||||||
|
@ -211,11 +287,25 @@ CSSPixelRect IntersectionObserver::root_intersection_rectangle() const
|
||||||
rect = CSSPixelRect(bounding_client_rect->x(), bounding_client_rect->y(), bounding_client_rect->width(), bounding_client_rect->height());
|
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
|
// 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
|
// 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
|
// 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
|
// edges, respectively, are offset by, with positive lengths indicating an outward offset. Percentages
|
||||||
// are resolved relative to the width of the undilated rectangle.
|
// are resolved relative to the width of the undilated rectangle.
|
||||||
|
DOM::Document* document = { nullptr };
|
||||||
|
if (intersection_root.has<GC::Root<DOM::Document>>()) {
|
||||||
|
document = intersection_root.get<GC::Root<DOM::Document>>().cell();
|
||||||
|
} else {
|
||||||
|
document = &intersection_root.get<GC::Root<DOM::Element>>().cell()->document();
|
||||||
|
}
|
||||||
|
if (m_document.has_value() && document->origin().is_same_origin(m_document->origin())) {
|
||||||
|
auto layout_node = intersection_root.visit([&](auto& elem) { return static_cast<GC::Root<DOM::Node>>(*elem)->layout_node(); });
|
||||||
|
rect.inflate(
|
||||||
|
m_root_margin[0].to_px(*layout_node, rect.height()),
|
||||||
|
m_root_margin[1].to_px(*layout_node, rect.width()),
|
||||||
|
m_root_margin[2].to_px(*layout_node, rect.height()),
|
||||||
|
m_root_margin[3].to_px(*layout_node, rect.width()));
|
||||||
|
}
|
||||||
|
|
||||||
return rect;
|
return rect;
|
||||||
}
|
}
|
||||||
|
@ -225,4 +315,69 @@ void IntersectionObserver::queue_entry(Badge<DOM::Document>, GC::Ref<Intersectio
|
||||||
m_queued_entries.append(entry);
|
m_queued_entries.append(entry);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// https://w3c.github.io/IntersectionObserver/#parse-a-margin
|
||||||
|
Optional<Vector<CSS::LengthPercentage>> IntersectionObserver::parse_a_margin(JS::Realm& realm, String margin_string)
|
||||||
|
{
|
||||||
|
// 1. Parse a list of component values marginString, storing the result as tokens.
|
||||||
|
auto tokens = CSS::Parser::Parser::create(CSS::Parser::ParsingContext { realm }, margin_string).parse_as_list_of_component_values();
|
||||||
|
|
||||||
|
// 2. Remove all whitespace tokens from tokens.
|
||||||
|
tokens.remove_all_matching([](auto componentValue) { return componentValue.is(CSS::Parser::Token::Type::Whitespace); });
|
||||||
|
|
||||||
|
// 3. If the length of tokens is greater than 4, return failure.
|
||||||
|
if (tokens.size() > 4) {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
// 4. If there are zero elements in tokens, set tokens to ["0px"].
|
||||||
|
if (tokens.size() == 0) {
|
||||||
|
tokens.append(CSS::Parser::Token::create_dimension(0, "px"_fly_string));
|
||||||
|
}
|
||||||
|
|
||||||
|
// 5. Replace each token in tokens:
|
||||||
|
// NOTE: In the spec, tokens miraculously changes type from a list of component values
|
||||||
|
// to a list of pixel lengths or percentages.
|
||||||
|
Vector<CSS::LengthPercentage> tokens_length_percentage;
|
||||||
|
for (auto const& token : tokens) {
|
||||||
|
// If token is an absolute length dimension token, replace it with a an equivalent pixel length.
|
||||||
|
if (token.is(CSS::Parser::Token::Type::Dimension)) {
|
||||||
|
auto length = CSS::Length(token.token().dimension_value(), CSS::Length::unit_from_name(token.token().dimension_unit()).value());
|
||||||
|
if (length.is_absolute()) {
|
||||||
|
length.absolute_length_to_px();
|
||||||
|
tokens_length_percentage.append(length);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// If token is a <percentage> token, replace it with an equivalent percentage.
|
||||||
|
if (token.is(CSS::Parser::Token::Type::Percentage)) {
|
||||||
|
tokens_length_percentage.append(CSS::Percentage(token.token().percentage()));
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
// Otherwise, return failure.
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
// 6.
|
||||||
|
switch (tokens_length_percentage.size()) {
|
||||||
|
// If there is one element in tokens, append three duplicates of that element to tokens.
|
||||||
|
case 1:
|
||||||
|
tokens_length_percentage.append(tokens_length_percentage.first());
|
||||||
|
tokens_length_percentage.append(tokens_length_percentage.first());
|
||||||
|
tokens_length_percentage.append(tokens_length_percentage.first());
|
||||||
|
break;
|
||||||
|
// Otherwise, if there are two elements are tokens, append a duplicate of each element to tokens.
|
||||||
|
case 2:
|
||||||
|
tokens_length_percentage.append(tokens_length_percentage.at(0));
|
||||||
|
tokens_length_percentage.append(tokens_length_percentage.at(1));
|
||||||
|
break;
|
||||||
|
// Otherwise, if there are three elements in tokens, append a duplicate of the second element to tokens.
|
||||||
|
case 3:
|
||||||
|
tokens_length_percentage.append(tokens_length_percentage.at(1));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 7. Return tokens.
|
||||||
|
return tokens_length_percentage;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,7 +16,10 @@ namespace Web::IntersectionObserver {
|
||||||
struct IntersectionObserverInit {
|
struct IntersectionObserverInit {
|
||||||
Optional<Variant<GC::Root<DOM::Element>, GC::Root<DOM::Document>>> root;
|
Optional<Variant<GC::Root<DOM::Element>, GC::Root<DOM::Document>>> root;
|
||||||
String root_margin { "0px"_string };
|
String root_margin { "0px"_string };
|
||||||
|
String scroll_margin { "0px"_string };
|
||||||
Variant<double, Vector<double>> threshold { 0 };
|
Variant<double, Vector<double>> threshold { 0 };
|
||||||
|
long delay = 0;
|
||||||
|
bool track_visibility = false;
|
||||||
};
|
};
|
||||||
|
|
||||||
// https://www.w3.org/TR/intersection-observer/#intersectionobserverregistration
|
// https://www.w3.org/TR/intersection-observer/#intersectionobserverregistration
|
||||||
|
@ -53,7 +56,11 @@ public:
|
||||||
Vector<GC::Ref<DOM::Element>> const& observation_targets() const { return m_observation_targets; }
|
Vector<GC::Ref<DOM::Element>> const& observation_targets() const { return m_observation_targets; }
|
||||||
|
|
||||||
Variant<GC::Root<DOM::Element>, GC::Root<DOM::Document>, Empty> root() const;
|
Variant<GC::Root<DOM::Element>, GC::Root<DOM::Document>, Empty> root() const;
|
||||||
|
String root_margin() const;
|
||||||
|
String scroll_margin() const;
|
||||||
Vector<double> const& thresholds() const { return m_thresholds; }
|
Vector<double> const& thresholds() const { return m_thresholds; }
|
||||||
|
long delay() const { return m_delay; }
|
||||||
|
bool track_visibility() const { return m_track_visibility; }
|
||||||
|
|
||||||
Variant<GC::Root<DOM::Element>, GC::Root<DOM::Document>> intersection_root() const;
|
Variant<GC::Root<DOM::Element>, GC::Root<DOM::Document>> intersection_root() const;
|
||||||
CSSPixelRect root_intersection_rectangle() const;
|
CSSPixelRect root_intersection_rectangle() const;
|
||||||
|
@ -63,21 +70,35 @@ public:
|
||||||
WebIDL::CallbackType& callback() { return *m_callback; }
|
WebIDL::CallbackType& callback() { return *m_callback; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
explicit IntersectionObserver(JS::Realm&, GC::Ptr<WebIDL::CallbackType> callback, Optional<Variant<GC::Root<DOM::Element>, GC::Root<DOM::Document>>> const& root, Vector<double>&& thresholds);
|
explicit IntersectionObserver(JS::Realm&, GC::Ptr<WebIDL::CallbackType> callback, Optional<Variant<GC::Root<DOM::Element>, GC::Root<DOM::Document>>> const& root, Vector<CSS::LengthPercentage> root_margin, Vector<CSS::LengthPercentage> scroll_margin, Vector<double>&& thresholds, double debug, bool track_visibility);
|
||||||
|
|
||||||
virtual void initialize(JS::Realm&) override;
|
virtual void initialize(JS::Realm&) override;
|
||||||
virtual void visit_edges(JS::Cell::Visitor&) override;
|
virtual void visit_edges(JS::Cell::Visitor&) override;
|
||||||
virtual void finalize() override;
|
virtual void finalize() override;
|
||||||
|
|
||||||
|
static Optional<Vector<CSS::LengthPercentage>> parse_a_margin(JS::Realm&, String);
|
||||||
|
|
||||||
// https://www.w3.org/TR/intersection-observer/#dom-intersectionobserver-callback-slot
|
// https://www.w3.org/TR/intersection-observer/#dom-intersectionobserver-callback-slot
|
||||||
GC::Ptr<WebIDL::CallbackType> m_callback;
|
GC::Ptr<WebIDL::CallbackType> m_callback;
|
||||||
|
|
||||||
// https://www.w3.org/TR/intersection-observer/#dom-intersectionobserver-root
|
// https://www.w3.org/TR/intersection-observer/#dom-intersectionobserver-root
|
||||||
GC::Ptr<DOM::Node> m_root;
|
GC::Ptr<DOM::Node> m_root;
|
||||||
|
|
||||||
|
// https://www.w3.org/TR/intersection-observer/#dom-intersectionobserver-rootmargin
|
||||||
|
Vector<CSS::LengthPercentage> m_root_margin;
|
||||||
|
|
||||||
|
// https://www.w3.org/TR/intersection-observer/#dom-intersectionobserver-scrollmargin
|
||||||
|
Vector<CSS::LengthPercentage> m_scroll_margin;
|
||||||
|
|
||||||
// https://www.w3.org/TR/intersection-observer/#dom-intersectionobserver-thresholds
|
// https://www.w3.org/TR/intersection-observer/#dom-intersectionobserver-thresholds
|
||||||
Vector<double> m_thresholds;
|
Vector<double> m_thresholds;
|
||||||
|
|
||||||
|
// https://w3c.github.io/IntersectionObserver/#dom-intersectionobserver-delay
|
||||||
|
long m_delay;
|
||||||
|
|
||||||
|
// https://w3c.github.io/IntersectionObserver/#dom-intersectionobserver-trackvisibility
|
||||||
|
bool m_track_visibility;
|
||||||
|
|
||||||
// https://www.w3.org/TR/intersection-observer/#dom-intersectionobserver-queuedentries-slot
|
// https://www.w3.org/TR/intersection-observer/#dom-intersectionobserver-queuedentries-slot
|
||||||
Vector<GC::Ref<IntersectionObserverEntry>> m_queued_entries;
|
Vector<GC::Ref<IntersectionObserverEntry>> m_queued_entries;
|
||||||
|
|
||||||
|
|
|
@ -11,9 +11,12 @@ callback IntersectionObserverCallback = undefined (sequence<IntersectionObserver
|
||||||
interface IntersectionObserver {
|
interface IntersectionObserver {
|
||||||
constructor(IntersectionObserverCallback callback, optional IntersectionObserverInit options = {});
|
constructor(IntersectionObserverCallback callback, optional IntersectionObserverInit options = {});
|
||||||
readonly attribute (Element or Document)? root;
|
readonly attribute (Element or Document)? root;
|
||||||
[FIXME] readonly attribute DOMString rootMargin;
|
readonly attribute DOMString rootMargin;
|
||||||
|
readonly attribute DOMString scrollMargin;
|
||||||
// FIXME: `sequence<double>` should be `FrozenArray<double>`
|
// FIXME: `sequence<double>` should be `FrozenArray<double>`
|
||||||
readonly attribute sequence<double> thresholds;
|
readonly attribute sequence<double> thresholds;
|
||||||
|
readonly attribute long delay;
|
||||||
|
readonly attribute boolean trackVisibility;
|
||||||
undefined observe(Element target);
|
undefined observe(Element target);
|
||||||
undefined unobserve(Element target);
|
undefined unobserve(Element target);
|
||||||
undefined disconnect();
|
undefined disconnect();
|
||||||
|
@ -24,6 +27,8 @@ interface IntersectionObserver {
|
||||||
dictionary IntersectionObserverInit {
|
dictionary IntersectionObserverInit {
|
||||||
(Element or Document)? root = null;
|
(Element or Document)? root = null;
|
||||||
DOMString rootMargin = "0px";
|
DOMString rootMargin = "0px";
|
||||||
// FIXME: DOMString scrollMargin = "0px";
|
DOMString scrollMargin = "0px";
|
||||||
(double or sequence<double>) threshold = 0;
|
(double or sequence<double>) threshold = 0;
|
||||||
|
long delay = 0;
|
||||||
|
boolean trackVisibility = false;
|
||||||
};
|
};
|
||||||
|
|
|
@ -0,0 +1,19 @@
|
||||||
|
Summary
|
||||||
|
|
||||||
|
Harness status: OK
|
||||||
|
|
||||||
|
Rerun
|
||||||
|
|
||||||
|
Found 9 tests
|
||||||
|
|
||||||
|
9 Pass
|
||||||
|
Details
|
||||||
|
Result Test Name MessagePass Observer attribute getters.
|
||||||
|
Pass observer.root
|
||||||
|
Pass observer.thresholds
|
||||||
|
Pass observer.rootMargin
|
||||||
|
Pass empty observer.thresholds
|
||||||
|
Pass whitespace observer.rootMargin
|
||||||
|
Pass set observer.root
|
||||||
|
Pass set observer.thresholds
|
||||||
|
Pass set observer.rootMargin
|
|
@ -0,0 +1,19 @@
|
||||||
|
Summary
|
||||||
|
|
||||||
|
Harness status: OK
|
||||||
|
|
||||||
|
Rerun
|
||||||
|
|
||||||
|
Found 9 tests
|
||||||
|
|
||||||
|
9 Pass
|
||||||
|
Details
|
||||||
|
Result Test Name MessagePass IntersectionObserver constructor with { threshold: [1.1] }
|
||||||
|
Pass IntersectionObserver constructor with { threshold: ["foo"] }
|
||||||
|
Pass IntersectionObserver constructor with { rootMargin: "1" }
|
||||||
|
Pass IntersectionObserver constructor with { rootMargin: "2em" }
|
||||||
|
Pass IntersectionObserver constructor with { rootMargin: "auto" }
|
||||||
|
Pass IntersectionObserver constructor with { rootMargin: "calc(1px + 2px)" }
|
||||||
|
Pass IntersectionObserver constructor with { rootMargin: "1px !important" }
|
||||||
|
Pass IntersectionObserver constructor with { rootMargin: "1px 1px 1px 1px 1px" }
|
||||||
|
Pass IntersectionObserver.observe("foo")
|
|
@ -0,0 +1,41 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<meta name="viewport" content="width=device-width,initial-scale=1">
|
||||||
|
<script src="../resources/testharness.js"></script>
|
||||||
|
<script src="../resources/testharnessreport.js"></script>
|
||||||
|
|
||||||
|
<div id="root"></div>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
test(function() {
|
||||||
|
var observer = new IntersectionObserver(function(e) {}, {});
|
||||||
|
test(function() { assert_equals(observer.root, null) },
|
||||||
|
"observer.root");
|
||||||
|
test(function() { assert_array_equals(observer.thresholds, [0]) },
|
||||||
|
"observer.thresholds");
|
||||||
|
test(function() { assert_equals(observer.rootMargin, "0px 0px 0px 0px") },
|
||||||
|
"observer.rootMargin");
|
||||||
|
|
||||||
|
observer = new IntersectionObserver(function(e) {}, {
|
||||||
|
rootMargin: " ",
|
||||||
|
threshold: []
|
||||||
|
});
|
||||||
|
test(function() { assert_array_equals(observer.thresholds, [0]) },
|
||||||
|
"empty observer.thresholds");
|
||||||
|
test(function() { assert_equals(observer.rootMargin, "0px 0px 0px 0px") },
|
||||||
|
"whitespace observer.rootMargin");
|
||||||
|
|
||||||
|
var rootDiv = document.getElementById("root");
|
||||||
|
observer = new IntersectionObserver(function(e) {}, {
|
||||||
|
root: rootDiv,
|
||||||
|
threshold: [0, 0.25, 0.5, 1.0],
|
||||||
|
rootMargin: "10% 20px"
|
||||||
|
});
|
||||||
|
test(function() { assert_equals(observer.root, rootDiv) },
|
||||||
|
"set observer.root");
|
||||||
|
test(function() { assert_array_equals(observer.thresholds, [0, 0.25, 0.5, 1.0]) },
|
||||||
|
"set observer.thresholds");
|
||||||
|
test(function() { assert_equals(observer.rootMargin, "10% 20px 10% 20px") },
|
||||||
|
"set observer.rootMargin");
|
||||||
|
}, "Observer attribute getters.");
|
||||||
|
|
||||||
|
</script>
|
|
@ -0,0 +1,61 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<meta name="viewport" content="width=device-width,initial-scale=1">
|
||||||
|
<script src="../resources/testharness.js"></script>
|
||||||
|
<script src="../resources/testharnessreport.js"></script>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
test(function () {
|
||||||
|
assert_throws_js(RangeError, function() {
|
||||||
|
new IntersectionObserver(e => {}, {threshold: [1.1]})
|
||||||
|
})
|
||||||
|
}, "IntersectionObserver constructor with { threshold: [1.1] }");
|
||||||
|
|
||||||
|
test(function () {
|
||||||
|
assert_throws_js(TypeError, function() {
|
||||||
|
new IntersectionObserver(e => {}, {threshold: ["foo"]})
|
||||||
|
})
|
||||||
|
}, 'IntersectionObserver constructor with { threshold: ["foo"] }');
|
||||||
|
|
||||||
|
test(function () {
|
||||||
|
assert_throws_dom("SYNTAX_ERR", function() {
|
||||||
|
new IntersectionObserver(e => {}, {rootMargin: "1"})
|
||||||
|
})
|
||||||
|
}, 'IntersectionObserver constructor with { rootMargin: "1" }');
|
||||||
|
|
||||||
|
test(function () {
|
||||||
|
assert_throws_dom("SYNTAX_ERR", function() {
|
||||||
|
new IntersectionObserver(e => {}, {rootMargin: "2em"})
|
||||||
|
})
|
||||||
|
}, 'IntersectionObserver constructor with { rootMargin: "2em" }');
|
||||||
|
|
||||||
|
test(function () {
|
||||||
|
assert_throws_dom("SYNTAX_ERR", function() {
|
||||||
|
new IntersectionObserver(e => {}, {rootMargin: "auto"})
|
||||||
|
})
|
||||||
|
}, 'IntersectionObserver constructor with { rootMargin: "auto" }');
|
||||||
|
|
||||||
|
test(function () {
|
||||||
|
assert_throws_dom("SYNTAX_ERR", function() {
|
||||||
|
new IntersectionObserver(e => {}, {rootMargin: "calc(1px + 2px)"})
|
||||||
|
})
|
||||||
|
}, 'IntersectionObserver constructor with { rootMargin: "calc(1px + 2px)" }');
|
||||||
|
|
||||||
|
test(function () {
|
||||||
|
assert_throws_dom("SYNTAX_ERR", function() {
|
||||||
|
new IntersectionObserver(e => {}, {rootMargin: "1px !important"})
|
||||||
|
})
|
||||||
|
}, 'IntersectionObserver constructor with { rootMargin: "1px !important" }');
|
||||||
|
|
||||||
|
test(function () {
|
||||||
|
assert_throws_dom("SYNTAX_ERR", function() {
|
||||||
|
new IntersectionObserver(e => {}, {rootMargin: "1px 1px 1px 1px 1px"})
|
||||||
|
})
|
||||||
|
}, 'IntersectionObserver constructor with { rootMargin: "1px 1px 1px 1px 1px" }');
|
||||||
|
|
||||||
|
test(function () {
|
||||||
|
assert_throws_js(TypeError, function() {
|
||||||
|
let observer = new IntersectionObserver(c => {}, {});
|
||||||
|
observer.observe("foo");
|
||||||
|
})
|
||||||
|
}, 'IntersectionObserver.observe("foo")');
|
||||||
|
</script>
|
Loading…
Add table
Add a link
Reference in a new issue