diff --git a/Userland/Libraries/LibWeb/DOM/Element.cpp b/Userland/Libraries/LibWeb/DOM/Element.cpp index 3da9d40dd8..90462df78e 100644 --- a/Userland/Libraries/LibWeb/DOM/Element.cpp +++ b/Userland/Libraries/LibWeb/DOM/Element.cpp @@ -7,6 +7,7 @@ #include #include #include +#include #include #include #include @@ -44,6 +45,7 @@ #include #include #include +#include #include #include #include @@ -903,4 +905,76 @@ WebIDL::ExceptionOr Element::insert_adjacent_text(String const& where, Str return {}; } +// https://w3c.github.io/csswg-drafts/cssom-view-1/#scroll-an-element-into-view +static void scroll_an_element_into_view(DOM::Element& element, Bindings::ScrollBehavior behavior, Bindings::ScrollLogicalPosition block, Bindings::ScrollLogicalPosition inline_) +{ + // FIXME: The below is ad-hoc, since we don't yet have scrollable elements. + // Return here and implement this according to spec once all overflow is made scrollable. + + (void)behavior; + (void)block; + (void)inline_; + + if (!element.document().browsing_context()) + return; + + auto* page = element.document().browsing_context()->page(); + if (!page) + return; + + // If this element doesn't have a layout node, we can't scroll it into view. + element.document().update_layout(); + if (!element.layout_node()) + return; + + // Find the nearest layout node that is a box (since we need a box to get a usable rect) + auto* layout_node = element.layout_node(); + while (layout_node && !layout_node->is_box()) + layout_node = layout_node->parent(); + + if (!layout_node) + return; + + page->client().page_did_request_scroll_into_view(verify_cast(*layout_node).paint_box()->absolute_padding_box_rect().to_rounded()); +} + +// https://w3c.github.io/csswg-drafts/cssom-view-1/#dom-element-scrollintoview +void Element::scroll_into_view(Optional> arg) +{ + // 1. Let behavior be "auto". + auto behavior = Bindings::ScrollBehavior::Auto; + + // 2. Let block be "start". + auto block = Bindings::ScrollLogicalPosition::Start; + + // 3. Let inline be "nearest". + auto inline_ = Bindings::ScrollLogicalPosition::Nearest; + + // 4. If arg is a ScrollIntoViewOptions dictionary, then: + if (arg.has_value() && arg->has()) { + // 1. Set behavior to the behavior dictionary member of options. + behavior = arg->get().behavior; + + // 2. Set block to the block dictionary member of options. + block = arg->get().block; + + // 3. Set inline to the inline dictionary member of options. + inline_ = arg->get().inline_; + } + // 5. Otherwise, if arg is false, then set block to "end". + else if (arg.has_value() && arg->has() && arg->get() == false) { + block = Bindings::ScrollLogicalPosition::End; + } + + // 6. If the element does not have any associated box, or is not available to user-agent features, then return. + document().update_layout(); + if (!layout_node()) + return; + + // 7. Scroll the element into view with behavior, block, and inline. + scroll_an_element_into_view(*this, behavior, block, inline_); + + // FIXME: 8. Optionally perform some other action that brings the element to the user’s attention. +} + } diff --git a/Userland/Libraries/LibWeb/DOM/Element.h b/Userland/Libraries/LibWeb/DOM/Element.h index 63f9176993..f3b8736d77 100644 --- a/Userland/Libraries/LibWeb/DOM/Element.h +++ b/Userland/Libraries/LibWeb/DOM/Element.h @@ -8,6 +8,7 @@ #include #include +#include #include #include #include @@ -25,6 +26,17 @@ namespace Web::DOM { +// https://w3c.github.io/csswg-drafts/cssom-view-1/#dictdef-scrolloptions +struct ScrollOptions { + Bindings::ScrollBehavior behavior { Bindings::ScrollBehavior::Auto }; +}; + +// https://w3c.github.io/csswg-drafts/cssom-view-1/#dictdef-scrollintoviewoptions +struct ScrollIntoViewOptions : public ScrollOptions { + Bindings::ScrollLogicalPosition block { Bindings::ScrollLogicalPosition::Start }; + Bindings::ScrollLogicalPosition inline_ { Bindings::ScrollLogicalPosition::Nearest }; +}; + class Element : public ParentNode , public ChildNode @@ -147,6 +159,9 @@ public: WebIDL::ExceptionOr> insert_adjacent_element(String const& where, JS::NonnullGCPtr element); WebIDL::ExceptionOr insert_adjacent_text(String const& where, String const& data); + // https://w3c.github.io/csswg-drafts/cssom-view-1/#dom-element-scrollintoview + void scroll_into_view(Optional>); + protected: Element(Document&, DOM::QualifiedName); virtual void initialize(JS::Realm&) override; diff --git a/Userland/Libraries/LibWeb/DOM/Element.idl b/Userland/Libraries/LibWeb/DOM/Element.idl index 3bc26ce704..f3617d5fea 100644 --- a/Userland/Libraries/LibWeb/DOM/Element.idl +++ b/Userland/Libraries/LibWeb/DOM/Element.idl @@ -9,6 +9,17 @@ #import #import +enum ScrollBehavior { "auto", "smooth" }; +dictionary ScrollOptions { + ScrollBehavior behavior = "auto"; +}; + +enum ScrollLogicalPosition { "start", "center", "end", "nearest" }; +dictionary ScrollIntoViewOptions : ScrollOptions { + ScrollLogicalPosition block = "start"; + ScrollLogicalPosition inline = "nearest"; +}; + // https://dom.spec.whatwg.org/#element interface Element : Node { readonly attribute DOMString? namespaceURI; @@ -56,6 +67,9 @@ interface Element : Node { [CEReactions] Element? insertAdjacentElement(DOMString where, Element element); undefined insertAdjacentText(DOMString where, DOMString data); [CEReactions] undefined insertAdjacentHTML(DOMString position, DOMString text); + + undefined scrollIntoView(optional (boolean or ScrollIntoViewOptions) arg = {}); + }; Element includes ParentNode;