Browse Source

LibWeb: Implement more IntersectionObserver attributes

Psychpsyo 7 months ago
parent
commit
3e536a4cd7

+ 5 - 0
Libraries/LibWeb/CSS/Parser/Parser.cpp

@@ -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;

+ 2 - 0
Libraries/LibWeb/CSS/Parser/Parser.h

@@ -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);

+ 169 - 14
Libraries/LibWeb/IntersectionObserver/IntersectionObserver.cpp

@@ -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
-    // 2. Set this’s internal [[callback]] slot to callback.
-    // 8. The thresholds attribute getter will return this sorted thresholds list.
-    // 9. Return this.
-    return realm.create<IntersectionObserver>(realm, callback, options.root, move(thresholds));
+    // 8. If thresholds is empty, append 0 to thresholds.
+    if (thresholds.is_empty()) {
+        thresholds.append(0);
+    }
+
+    // 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
-    //        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.
+    // 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.
+    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;
+}
+
 }
 }

+ 22 - 1
Libraries/LibWeb/IntersectionObserver/IntersectionObserver.h

@@ -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;
 
 

+ 7 - 2
Libraries/LibWeb/IntersectionObserver/IntersectionObserver.idl

@@ -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;
 };
 };

+ 19 - 0
Tests/LibWeb/Text/expected/wpt-import/intersection-observer/observer-attributes.txt

@@ -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	

+ 19 - 0
Tests/LibWeb/Text/expected/wpt-import/intersection-observer/observer-exceptions.txt

@@ -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")	

+ 41 - 0
Tests/LibWeb/Text/input/wpt-import/intersection-observer/observer-attributes.html

@@ -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>

+ 61 - 0
Tests/LibWeb/Text/input/wpt-import/intersection-observer/observer-exceptions.html

@@ -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>