diff --git a/Tests/LibWeb/Text/expected/HTML/Element-checkVisibility.txt b/Tests/LibWeb/Text/expected/HTML/Element-checkVisibility.txt new file mode 100644 index 00000000000..4d3b83b28bf --- /dev/null +++ b/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 \ No newline at end of file diff --git a/Tests/LibWeb/Text/input/HTML/Element-checkVisibility.html b/Tests/LibWeb/Text/input/HTML/Element-checkVisibility.html new file mode 100644 index 00000000000..cae83bd0445 --- /dev/null +++ b/Tests/LibWeb/Text/input/HTML/Element-checkVisibility.html @@ -0,0 +1,71 @@ + + + + + +
+
+
+ +
+
+ +
+
+ +
+ + + diff --git a/Userland/Libraries/LibWeb/DOM/Element.cpp b/Userland/Libraries/LibWeb/DOM/Element.cpp index 0134a1f9155..d055fbd0e56 100644 --- a/Userland/Libraries/LibWeb/DOM/Element.cpp +++ b/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 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); diff --git a/Userland/Libraries/LibWeb/DOM/Element.h b/Userland/Libraries/LibWeb/DOM/Element.h index 1d92498b83e..e546feb2d21 100644 --- a/Userland/Libraries/LibWeb/DOM/Element.h +++ b/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); + void register_intersection_observer(Badge, IntersectionObserver::IntersectionObserverRegistration); void unregister_intersection_observer(Badge, JS::NonnullGCPtr); IntersectionObserver::IntersectionObserverRegistration& get_intersection_observer_registration(Badge, IntersectionObserver::IntersectionObserver const&); diff --git a/Userland/Libraries/LibWeb/DOM/Element.idl b/Userland/Libraries/LibWeb/DOM/Element.idl index e1e63943809..1e9a8769a60 100644 --- a/Userland/Libraries/LibWeb/DOM/Element.idl +++ b/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 = {});