Pārlūkot izejas kodu

LibWeb: Implement Element::check_visibility

Edwin Hoksberg 1 gadu atpakaļ
vecāks
revīzija
5f154ba372

+ 8 - 0
Tests/LibWeb/Text/expected/HTML/Element-checkVisibility.txt

@@ -0,0 +1,8 @@
+         display-none visible: false
+content-visibility-parent visible: true
+content-visibility-child visible: false
+opacity-hidden visible: false
+opacity-visible visible: true
+visibility-hidden visible: false
+visibility-visible visible: true
+content-visibility-auto-hidden visible: true

+ 71 - 0
Tests/LibWeb/Text/input/HTML/Element-checkVisibility.html

@@ -0,0 +1,71 @@
+<!DOCTYPE html>
+<style>
+    .display-none {
+        display: none;
+    }
+
+    .content-visibility-parent {
+        content-visibility: hidden;
+    }
+
+    .opacity-hidden {
+        opacity: 0;
+    }
+    .opacity-visible {
+        opacity: 0.1;
+    }
+
+    .visibility-hidden {
+        visibility: hidden;
+    }
+    .visibility-visible {
+        visibility: visible;
+    }
+
+    .content-visibility-auto-hidden {
+        content-visibility: auto;
+    }
+</style>
+
+<div class="display-none"></div>
+
+<div class="content-visibility-parent">
+    <div class="content-visibility-child"></div>
+</div>
+
+<div class="opacity-hidden"></div>
+<div class="opacity-visible"></div>
+
+<div class="visibility-hidden"></div>
+<div class="visibility-visible"></div>
+
+<div class="content-visibility-auto-hidden"></div>
+
+<script src="../include.js"></script>
+<script>
+    test(() => {
+        const displayNone = document.querySelector('.display-none');
+        println("display-none visible: " + displayNone.checkVisibility());
+
+        const contentVisibilityParent = document.querySelector('.content-visibility-parent');
+        println("content-visibility-parent visible: " + contentVisibilityParent.checkVisibility());
+
+        const contentVisibilityChild = document.querySelector('.content-visibility-child');
+        println("content-visibility-child visible: " + contentVisibilityChild.checkVisibility());
+
+        const opacityHidden = document.querySelector('.opacity-hidden');
+        println("opacity-hidden visible: " + opacityHidden.checkVisibility({opacityProperty: true}));
+
+        const opacityVisible = document.querySelector('.opacity-visible');
+        println("opacity-visible visible: " + opacityVisible.checkVisibility({opacityProperty: true}));
+
+        const visibilityHidden = document.querySelector('.visibility-hidden');
+        println("visibility-hidden visible: " + visibilityHidden.checkVisibility({visibilityProperty: true}));
+
+        const visibilityVisible = document.querySelector('.visibility-visible');
+        println("visibility-visible visible: " + visibilityVisible.checkVisibility({visibilityProperty: true}));
+
+        const contentVisibilityAutoHidden = document.querySelector('.content-visibility-auto-hidden');
+        println("content-visibility-auto-hidden visible: " + contentVisibilityAutoHidden.checkVisibility({contentVisibilityAuto: true}));
+    });
+</script>

+ 48 - 0
Userland/Libraries/LibWeb/DOM/Element.cpp

@@ -2344,6 +2344,54 @@ void Element::scroll_by(HTML::ScrollToOptions options)
     scroll(options);
 }
 
+// https://drafts.csswg.org/cssom-view-1/#dom-element-checkvisibility
+bool Element::check_visibility(Optional<CheckVisibilityOptions> options)
+{
+    // NOTE: Ensure that layout is up-to-date before looking at metrics.
+    document().update_layout();
+
+    // 1. If this does not have an associated box, return false.
+    if (!paintable_box())
+        return false;
+
+    // 2. If an ancestor of this in the flat tree has content-visibility: hidden, return false.
+    for (auto* element = parent_element(); element; element = element->parent_element()) {
+        if (element->computed_css_values()->content_visibility() == CSS::ContentVisibility::Hidden)
+            return false;
+    }
+
+    // AD-HOC: Since the rest of the steps use the options, we can return early if we haven't been given any options.
+    if (!options.has_value())
+        return true;
+
+    // 3. If either the opacityProperty or the checkOpacity dictionary members of options are true, and this, or an ancestor of this in the flat tree, has a computed opacity value of 0, return false.
+    if (options->opacity_property || options->check_opacity) {
+        for (auto* element = this; element; element = element->parent_element()) {
+            if (element->computed_css_values()->opacity() == 0.0f)
+                return false;
+        }
+    }
+
+    // 4. If either the visibilityProperty or the checkVisibilityCSS dictionary members of options are true, and this is invisible, return false.
+    if (options->visibility_property || options->check_visibility_css) {
+        if (computed_css_values()->visibility() == CSS::Visibility::Hidden)
+            return false;
+    }
+
+    // 5. If the contentVisibilityAuto dictionary member of options is true and an ancestor of this in the flat tree skips its contents due to content-visibility: auto, return false.
+    // FIXME: Currently we do not skip any content if content-visibility is auto: https://drafts.csswg.org/css-contain-2/#proximity-to-the-viewport
+    auto const skipped_contents_due_to_content_visibility_auto = false;
+    if (options->content_visibility_auto && skipped_contents_due_to_content_visibility_auto) {
+        for (auto* element = this; element; element = element->parent_element()) {
+            if (element->computed_css_values()->content_visibility() == CSS::ContentVisibility::Auto)
+                return false;
+        }
+    }
+
+    // 6. Return true.
+    return true;
+}
+
 bool Element::id_reference_exists(String const& id_reference) const
 {
     return document().get_element_by_id(id_reference);

+ 11 - 0
Userland/Libraries/LibWeb/DOM/Element.h

@@ -48,6 +48,15 @@ struct ScrollIntoViewOptions : public HTML::ScrollOptions {
     Bindings::ScrollLogicalPosition inline_ { Bindings::ScrollLogicalPosition::Nearest };
 };
 
+// https://drafts.csswg.org/cssom-view-1/#dictdef-checkvisibilityoptions
+struct CheckVisibilityOptions {
+    bool check_opacity = false;
+    bool check_visibility_css = false;
+    bool content_visibility_auto = false;
+    bool opacity_property = false;
+    bool visibility_property = false;
+};
+
 // https://html.spec.whatwg.org/multipage/custom-elements.html#upgrade-reaction
 // An upgrade reaction, which will upgrade the custom element and contains a custom element definition; or
 struct CustomElementUpgradeReaction {
@@ -354,6 +363,8 @@ public:
     void scroll_by(HTML::ScrollToOptions);
     void scroll_by(double x, double y);
 
+    bool check_visibility(Optional<CheckVisibilityOptions>);
+
     void register_intersection_observer(Badge<IntersectionObserver::IntersectionObserver>, IntersectionObserver::IntersectionObserverRegistration);
     void unregister_intersection_observer(Badge<IntersectionObserver::IntersectionObserver>, JS::NonnullGCPtr<IntersectionObserver::IntersectionObserver>);
     IntersectionObserver::IntersectionObserverRegistration& get_intersection_observer_registration(Badge<DOM::Document>, IntersectionObserver::IntersectionObserver const&);

+ 10 - 1
Userland/Libraries/LibWeb/DOM/Element.idl

@@ -20,6 +20,15 @@ dictionary ScrollIntoViewOptions : ScrollOptions {
     ScrollLogicalPosition inline = "nearest";
 };
 
+// https://drafts.csswg.org/cssom-view-1/#dictdef-checkvisibilityoptions
+dictionary CheckVisibilityOptions {
+    boolean checkOpacity = false;
+    boolean checkVisibilityCSS = false;
+    boolean contentVisibilityAuto = false;
+    boolean opacityProperty = false;
+    boolean visibilityProperty = false;
+};
+
 // https://dom.spec.whatwg.org/#element
 [Exposed=Window]
 interface Element : Node {
@@ -74,7 +83,7 @@ interface Element : Node {
     DOMRectList getClientRects();
     DOMRect getBoundingClientRect();
 
-    [FIXME] boolean checkVisibility(optional CheckVisibilityOptions options = {});
+    boolean checkVisibility(optional CheckVisibilityOptions options = {});
 
     undefined scrollIntoView(optional (boolean or ScrollIntoViewOptions) arg = {});
     undefined scroll(optional ScrollToOptions options = {});