summaryrefslogtreecommitdiff
path: root/Userland/Libraries/LibWeb/DOM
diff options
context:
space:
mode:
Diffstat (limited to 'Userland/Libraries/LibWeb/DOM')
-rw-r--r--Userland/Libraries/LibWeb/DOM/Document.h1
-rw-r--r--Userland/Libraries/LibWeb/DOM/Element.cpp249
-rw-r--r--Userland/Libraries/LibWeb/DOM/Element.h10
-rw-r--r--Userland/Libraries/LibWeb/DOM/Element.idl4
4 files changed, 264 insertions, 0 deletions
diff --git a/Userland/Libraries/LibWeb/DOM/Document.h b/Userland/Libraries/LibWeb/DOM/Document.h
index 3a0ce51dfa..3cf09999a2 100644
--- a/Userland/Libraries/LibWeb/DOM/Document.h
+++ b/Userland/Libraries/LibWeb/DOM/Document.h
@@ -298,6 +298,7 @@ public:
WebIDL::ExceptionOr<void> close();
HTML::Window* default_view() { return m_window.ptr(); }
+ HTML::Window const* default_view() const { return m_window.ptr(); }
String const& content_type() const { return m_content_type; }
void set_content_type(String const& content_type) { m_content_type = content_type; }
diff --git a/Userland/Libraries/LibWeb/DOM/Element.cpp b/Userland/Libraries/LibWeb/DOM/Element.cpp
index adb878149e..1531adec5c 100644
--- a/Userland/Libraries/LibWeb/DOM/Element.cpp
+++ b/Userland/Libraries/LibWeb/DOM/Element.cpp
@@ -764,6 +764,255 @@ void Element::set_tab_index(i32 tab_index)
MUST(set_attribute(HTML::AttributeNames::tabindex, String::number(tab_index)));
}
+// https://drafts.csswg.org/cssom-view/#potentially-scrollable
+bool Element::is_potentially_scrollable() const
+{
+ // NOTE: Ensure that layout is up-to-date before looking at metrics.
+ const_cast<Document&>(document()).update_layout();
+
+ // An element body (which will be the body element) is potentially scrollable if all of the following conditions are true:
+ VERIFY(is<HTML::HTMLBodyElement>(this) || is<HTML::HTMLFrameSetElement>(this));
+
+ // Since this should always be the body element, the body element must have a <html> element parent. See Document::body().
+ VERIFY(parent());
+
+ // - body has an associated box.
+ // - body’s parent element’s computed value of the overflow-x or overflow-y properties is neither visible nor clip.
+ // - body’s computed value of the overflow-x or overflow-y properties is neither visible nor clip.
+ return layout_node()
+ && (parent()->layout_node()
+ && parent()->layout_node()->computed_values().overflow_x() != CSS::Overflow::Visible && parent()->layout_node()->computed_values().overflow_x() != CSS::Overflow::Clip
+ && parent()->layout_node()->computed_values().overflow_y() != CSS::Overflow::Visible && parent()->layout_node()->computed_values().overflow_y() != CSS::Overflow::Clip)
+ && (layout_node()->computed_values().overflow_x() != CSS::Overflow::Visible && layout_node()->computed_values().overflow_x() != CSS::Overflow::Clip
+ && layout_node()->computed_values().overflow_y() != CSS::Overflow::Visible && layout_node()->computed_values().overflow_y() != CSS::Overflow::Clip);
+}
+
+// https://drafts.csswg.org/cssom-view/#dom-element-scrolltop
+double Element::scroll_top() const
+{
+ // 1. Let document be the element’s node document.
+ auto& document = this->document();
+
+ // 2. If document is not the active document, return zero and terminate these steps.
+ if (!document.is_active())
+ return 0.0;
+
+ // 3. Let window be the value of document’s defaultView attribute.
+ auto* window = document.default_view();
+
+ // 4. If window is null, return zero and terminate these steps.
+ if (!window)
+ return 0.0;
+
+ // 5. If the element is the root element and document is in quirks mode, return zero and terminate these steps.
+ if (document.document_element() == this && document.in_quirks_mode())
+ return 0.0;
+
+ // NOTE: Ensure that layout is up-to-date before looking at metrics.
+ const_cast<Document&>(document).update_layout();
+
+ // 6. If the element is the root element return the value of scrollY on window.
+ if (document.document_element() == this)
+ return window->scroll_y();
+
+ // 7. If the element is the body element, document is in quirks mode, and the element is not potentially scrollable, return the value of scrollY on window.
+ if (document.body() == this && document.in_quirks_mode() && !is_potentially_scrollable())
+ return window->scroll_y();
+
+ // 8. If the element does not have any associated box, return zero and terminate these steps.
+ if (!layout_node() || !is<Layout::BlockContainer>(layout_node()))
+ return 0.0;
+
+ auto const* block_container = static_cast<Layout::BlockContainer const*>(layout_node());
+
+ // 9. Return the y-coordinate of the scrolling area at the alignment point with the top of the padding edge of the element.
+ // FIXME: Is this correct?
+ return block_container->scroll_offset().y();
+}
+
+double Element::scroll_left() const
+{
+ // 1. Let document be the element’s node document.
+ auto& document = this->document();
+
+ // 2. If document is not the active document, return zero and terminate these steps.
+ if (!document.is_active())
+ return 0.0;
+
+ // 3. Let window be the value of document’s defaultView attribute.
+ auto* window = document.default_view();
+
+ // 4. If window is null, return zero and terminate these steps.
+ if (!window)
+ return 0.0;
+
+ // 5. If the element is the root element and document is in quirks mode, return zero and terminate these steps.
+ if (document.document_element() == this && document.in_quirks_mode())
+ return 0.0;
+
+ // NOTE: Ensure that layout is up-to-date before looking at metrics.
+ const_cast<Document&>(document).update_layout();
+
+ // 6. If the element is the root element return the value of scrollX on window.
+ if (document.document_element() == this)
+ return window->scroll_x();
+
+ // 7. If the element is the body element, document is in quirks mode, and the element is not potentially scrollable, return the value of scrollX on window.
+ if (document.body() == this && document.in_quirks_mode() && !is_potentially_scrollable())
+ return window->scroll_x();
+
+ // 8. If the element does not have any associated box, return zero and terminate these steps.
+ if (!layout_node() || !is<Layout::BlockContainer>(layout_node()))
+ return 0.0;
+
+ auto const* block_container = static_cast<Layout::BlockContainer const*>(layout_node());
+
+ // 9. Return the x-coordinate of the scrolling area at the alignment point with the left of the padding edge of the element.
+ // FIXME: Is this correct?
+ return block_container->scroll_offset().x();
+}
+
+// https://drafts.csswg.org/cssom-view/#dom-element-scrollleft
+void Element::set_scroll_left(double x)
+{
+ // 1. Let x be the given value.
+
+ // 2. Normalize non-finite values for x.
+ if (!isfinite(x))
+ x = 0.0;
+
+ // 3. Let document be the element’s node document.
+ auto& document = this->document();
+
+ // 4. If document is not the active document, terminate these steps.
+ if (!document.is_active())
+ return;
+
+ // 5. Let window be the value of document’s defaultView attribute.
+ auto* window = document.default_view();
+
+ // 6. If window is null, terminate these steps.
+ if (!window)
+ return;
+
+ // 7. If the element is the root element and document is in quirks mode, terminate these steps.
+ if (document.document_element() == this && document.in_quirks_mode())
+ return;
+
+ // NOTE: Ensure that layout is up-to-date before looking at metrics or scrolling the page.
+ const_cast<Document&>(document).update_layout();
+
+ // 8. If the element is the root element invoke scroll() on window with x as first argument and scrollY on window as second argument, and terminate these steps.
+ if (document.document_element() == this) {
+ // FIXME: Implement this in terms of invoking scroll() on window.
+ if (auto* page = document.page())
+ page->client().page_did_request_scroll_to({ static_cast<float>(x), window->scroll_y() });
+
+ return;
+ }
+
+ // 9. If the element is the body element, document is in quirks mode, and the element is not potentially scrollable, invoke scroll() on window with x as first argument and scrollY on window as second argument, and terminate these steps.
+ if (document.body() == this && document.in_quirks_mode() && !is_potentially_scrollable()) {
+ // FIXME: Implement this in terms of invoking scroll() on window.
+ if (auto* page = document.page())
+ page->client().page_did_request_scroll_to({ static_cast<float>(x), window->scroll_y() });
+
+ return;
+ }
+
+ // 10. If the element does not have any associated box, the element has no associated scrolling box, or the element has no overflow, terminate these steps.
+ if (!layout_node() || !is<Layout::BlockContainer>(layout_node()))
+ return;
+
+ auto* block_container = static_cast<Layout::BlockContainer*>(layout_node());
+ if (!block_container->is_scrollable())
+ return;
+
+ // FIXME: or the element has no overflow.
+
+ // 11. Scroll the element to x,scrollTop, with the scroll behavior being "auto".
+ // FIXME: Implement this in terms of calling "scroll the element".
+ auto scroll_offset = block_container->scroll_offset();
+ scroll_offset.set_x(static_cast<float>(x));
+ block_container->set_scroll_offset(scroll_offset);
+}
+
+void Element::set_scroll_top(double y)
+{
+ // 1. Let y be the given value.
+
+ // 2. Normalize non-finite values for y.
+ if (!isfinite(y))
+ y = 0.0;
+
+ // 3. Let document be the element’s node document.
+ auto& document = this->document();
+
+ // 4. If document is not the active document, terminate these steps.
+ if (!document.is_active())
+ return;
+
+ // 5. Let window be the value of document’s defaultView attribute.
+ auto* window = document.default_view();
+
+ // 6. If window is null, terminate these steps.
+ if (!window)
+ return;
+
+ // 7. If the element is the root element and document is in quirks mode, terminate these steps.
+ if (document.document_element() == this && document.in_quirks_mode())
+ return;
+
+ // NOTE: Ensure that layout is up-to-date before looking at metrics or scrolling the page.
+ const_cast<Document&>(document).update_layout();
+
+ // 8. If the element is the root element invoke scroll() on window with scrollX on window as first argument and y as second argument, and terminate these steps.
+ if (document.document_element() == this) {
+ // FIXME: Implement this in terms of invoking scroll() on window.
+ if (auto* page = document.page())
+ page->client().page_did_request_scroll_to({ window->scroll_x(), static_cast<float>(y) });
+
+ return;
+ }
+
+ // 9. If the element is the body element, document is in quirks mode, and the element is not potentially scrollable, invoke scroll() on window with scrollX as first argument and y as second argument, and terminate these steps.
+ if (document.body() == this && document.in_quirks_mode() && !is_potentially_scrollable()) {
+ // FIXME: Implement this in terms of invoking scroll() on window.
+ if (auto* page = document.page())
+ page->client().page_did_request_scroll_to({ window->scroll_x(), static_cast<float>(y) });
+
+ return;
+ }
+
+ // 10. If the element does not have any associated box, the element has no associated scrolling box, or the element has no overflow, terminate these steps.
+ if (!layout_node() || !is<Layout::BlockContainer>(layout_node()))
+ return;
+
+ auto* block_container = static_cast<Layout::BlockContainer*>(layout_node());
+ if (!block_container->is_scrollable())
+ return;
+
+ // FIXME: or the element has no overflow.
+
+ // 11. Scroll the element to scrollLeft,y, with the scroll behavior being "auto".
+ // FIXME: Implement this in terms of calling "scroll the element".
+ auto scroll_offset = block_container->scroll_offset();
+ scroll_offset.set_y(static_cast<float>(y));
+ block_container->set_scroll_offset(scroll_offset);
+}
+
+int Element::scroll_width() const
+{
+ dbgln("FIXME: Implement Element::scroll_width() (called on element: {})", debug_description());
+ return 0;
+}
+
+int Element::scroll_height() const
+{
+ dbgln("FIXME: Implement Element::scroll_height() (called on element: {})", debug_description());
+ return 0;
+}
+
// https://html.spec.whatwg.org/multipage/semantics-other.html#concept-element-disabled
bool Element::is_actually_disabled() const
{
diff --git a/Userland/Libraries/LibWeb/DOM/Element.h b/Userland/Libraries/LibWeb/DOM/Element.h
index c58f89b8ef..e54e89de5a 100644
--- a/Userland/Libraries/LibWeb/DOM/Element.h
+++ b/Userland/Libraries/LibWeb/DOM/Element.h
@@ -159,6 +159,16 @@ public:
i32 tab_index() const;
void set_tab_index(i32 tab_index);
+ bool is_potentially_scrollable() const;
+
+ double scroll_top() const;
+ double scroll_left() const;
+ void set_scroll_top(double y);
+ void set_scroll_left(double x);
+
+ int scroll_width() const;
+ int scroll_height() const;
+
bool is_actually_disabled() const;
WebIDL::ExceptionOr<JS::GCPtr<Element>> insert_adjacent_element(String const& where, JS::NonnullGCPtr<Element> element);
diff --git a/Userland/Libraries/LibWeb/DOM/Element.idl b/Userland/Libraries/LibWeb/DOM/Element.idl
index addfe89d8a..46a98c7145 100644
--- a/Userland/Libraries/LibWeb/DOM/Element.idl
+++ b/Userland/Libraries/LibWeb/DOM/Element.idl
@@ -63,6 +63,10 @@ interface Element : Node {
DOMRect getBoundingClientRect();
DOMRectList getClientRects();
+ attribute unrestricted double scrollTop;
+ attribute unrestricted double scrollLeft;
+ readonly attribute long scrollWidth;
+ readonly attribute long scrollHeight;
readonly attribute long clientTop;
readonly attribute long clientLeft;
readonly attribute long clientWidth;