diff --git a/Meta/gn/secondary/Userland/Libraries/LibWeb/CSS/BUILD.gn b/Meta/gn/secondary/Userland/Libraries/LibWeb/CSS/BUILD.gn index 7a8f4ce605f..1ae917fdd2c 100644 --- a/Meta/gn/secondary/Userland/Libraries/LibWeb/CSS/BUILD.gn +++ b/Meta/gn/secondary/Userland/Libraries/LibWeb/CSS/BUILD.gn @@ -57,5 +57,6 @@ source_set("CSS") { "StyleValue.cpp", "Supports.cpp", "Time.cpp", + "VisualViewport.cpp", ] } diff --git a/Userland/Libraries/LibWeb/CMakeLists.txt b/Userland/Libraries/LibWeb/CMakeLists.txt index 538683710f5..61b47b3e4d8 100644 --- a/Userland/Libraries/LibWeb/CMakeLists.txt +++ b/Userland/Libraries/LibWeb/CMakeLists.txt @@ -123,6 +123,7 @@ set(SOURCES CSS/Supports.cpp CSS/SyntaxHighlighter/SyntaxHighlighter.cpp CSS/Time.cpp + CSS/VisualViewport.cpp Cookie/Cookie.cpp Cookie/ParsedCookie.cpp DOM/AbortController.cpp diff --git a/Userland/Libraries/LibWeb/CSS/VisualViewport.cpp b/Userland/Libraries/LibWeb/CSS/VisualViewport.cpp new file mode 100644 index 00000000000..bd4f9ba066e --- /dev/null +++ b/Userland/Libraries/LibWeb/CSS/VisualViewport.cpp @@ -0,0 +1,154 @@ +/* + * Copyright (c) 2023, Andreas Kling + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +namespace Web::CSS { + +WebIDL::ExceptionOr> VisualViewport::create(DOM::Document& document) +{ + return MUST_OR_THROW_OOM(document.heap().allocate(document.realm(), document)); +} + +VisualViewport::VisualViewport(DOM::Document& document) + : DOM::EventTarget(document.realm()) + , m_document(document) +{ +} + +JS::ThrowCompletionOr VisualViewport::initialize(JS::Realm& realm) +{ + MUST_OR_THROW_OOM(Base::initialize(realm)); + set_prototype(&Bindings::ensure_web_prototype(realm, "VisualViewport")); + + return {}; +} + +void VisualViewport::visit_edges(Cell::Visitor& visitor) +{ + Base::visit_edges(visitor); + visitor.visit(m_document); +} + +// https://drafts.csswg.org/cssom-view/#dom-visualviewport-offsetleft +double VisualViewport::offset_left() const +{ + // 1. If the visual viewport’s associated document is not fully active, return 0. + if (!m_document->is_fully_active()) + return 0; + + // 2. Otherwise, return the offset of the left edge of the visual viewport from the left edge of the layout viewport. + VERIFY(m_document->browsing_context()); + return m_document->browsing_context()->viewport_rect().left().to_double(); +} + +// https://drafts.csswg.org/cssom-view/#dom-visualviewport-offsettop +double VisualViewport::offset_top() const +{ + // 1. If the visual viewport’s associated document is not fully active, return 0. + if (!m_document->is_fully_active()) + return 0; + + // 2. Otherwise, return the offset of the top edge of the visual viewport from the top edge of the layout viewport. + VERIFY(m_document->browsing_context()); + return m_document->browsing_context()->viewport_rect().top().to_double(); +} + +// https://drafts.csswg.org/cssom-view/#dom-visualviewport-pageleft +double VisualViewport::page_left() const +{ + // 1. If the visual viewport’s associated document is not fully active, return 0. + if (!m_document->is_fully_active()) + return 0; + + // FIXME: 2. Otherwise, return the offset of the left edge of the visual viewport + // from the left edge of the initial containing block of the layout viewport’s document. + return offset_left(); +} + +// https://drafts.csswg.org/cssom-view/#dom-visualviewport-pagetop +double VisualViewport::page_top() const +{ + // 1. If the visual viewport’s associated document is not fully active, return 0. + if (!m_document->is_fully_active()) + return 0; + + // FIXME: 2. Otherwise, return the offset of the top edge of the visual viewport + // from the top edge of the initial containing block of the layout viewport’s document. + return offset_top(); +} + +// https://drafts.csswg.org/cssom-view/#dom-visualviewport-width +double VisualViewport::width() const +{ + // 1. If the visual viewport’s associated document is not fully active, return 0. + if (!m_document->is_fully_active()) + return 0; + + // 2. Otherwise, return the width of the visual viewport + // FIXME: excluding the width of any rendered vertical classic scrollbar that is fixed to the visual viewport. + VERIFY(m_document->browsing_context()); + return m_document->browsing_context()->viewport_rect().width().to_double(); +} + +// https://drafts.csswg.org/cssom-view/#dom-visualviewport-height +double VisualViewport::height() const +{ + // 1. If the visual viewport’s associated document is not fully active, return 0. + if (!m_document->is_fully_active()) + return 0; + + // 2. Otherwise, return the height of the visual viewport + // FIXME: excluding the height of any rendered vertical classic scrollbar that is fixed to the visual viewport. + VERIFY(m_document->browsing_context()); + return m_document->browsing_context()->viewport_rect().height().to_double(); +} + +// https://drafts.csswg.org/cssom-view/#dom-visualviewport-scale +double VisualViewport::scale() const +{ + // FIXME: Implement. + return 1; +} + +void VisualViewport::set_onresize(WebIDL::CallbackType* event_handler) +{ + set_event_handler_attribute(HTML::EventNames::resize, event_handler); +} + +WebIDL::CallbackType* VisualViewport::onresize() +{ + return event_handler_attribute(HTML::EventNames::resize); +} + +void VisualViewport::set_onscroll(WebIDL::CallbackType* event_handler) +{ + set_event_handler_attribute(HTML::EventNames::scroll, event_handler); +} + +WebIDL::CallbackType* VisualViewport::onscroll() +{ + return event_handler_attribute(HTML::EventNames::scroll); +} + +void VisualViewport::set_onscrollend(WebIDL::CallbackType* event_handler) +{ + set_event_handler_attribute(HTML::EventNames::scrollend, event_handler); +} + +WebIDL::CallbackType* VisualViewport::onscrollend() +{ + return event_handler_attribute(HTML::EventNames::scrollend); +} + +} diff --git a/Userland/Libraries/LibWeb/CSS/VisualViewport.h b/Userland/Libraries/LibWeb/CSS/VisualViewport.h new file mode 100644 index 00000000000..56e41373825 --- /dev/null +++ b/Userland/Libraries/LibWeb/CSS/VisualViewport.h @@ -0,0 +1,49 @@ +/* + * Copyright (c) 2023, Andreas Kling + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#pragma once + +#include + +namespace Web::CSS { + +// https://drafts.csswg.org/cssom-view/#visualviewport +class VisualViewport final : public DOM::EventTarget { + WEB_PLATFORM_OBJECT(VisualViewport, DOM::EventTarget); + +public: + static WebIDL::ExceptionOr> create(DOM::Document&); + + virtual ~VisualViewport() override = default; + + [[nodiscard]] double offset_left() const; + [[nodiscard]] double offset_top() const; + + [[nodiscard]] double page_left() const; + [[nodiscard]] double page_top() const; + + [[nodiscard]] double width() const; + [[nodiscard]] double height() const; + + [[nodiscard]] double scale() const; + + void set_onresize(WebIDL::CallbackType*); + WebIDL::CallbackType* onresize(); + void set_onscroll(WebIDL::CallbackType*); + WebIDL::CallbackType* onscroll(); + void set_onscrollend(WebIDL::CallbackType*); + WebIDL::CallbackType* onscrollend(); + +private: + explicit VisualViewport(DOM::Document&); + + virtual JS::ThrowCompletionOr initialize(JS::Realm&) override; + virtual void visit_edges(Cell::Visitor&) override; + + JS::NonnullGCPtr m_document; +}; + +} diff --git a/Userland/Libraries/LibWeb/CSS/VisualViewport.idl b/Userland/Libraries/LibWeb/CSS/VisualViewport.idl new file mode 100644 index 00000000000..63d3c5515f2 --- /dev/null +++ b/Userland/Libraries/LibWeb/CSS/VisualViewport.idl @@ -0,0 +1,21 @@ +#import + +[Exposed=Window] +interface VisualViewport : EventTarget { + + readonly attribute double offsetLeft; + readonly attribute double offsetTop; + + readonly attribute double pageLeft; + readonly attribute double pageTop; + + readonly attribute double width; + readonly attribute double height; + + readonly attribute double scale; + + attribute EventHandler onresize; + attribute EventHandler onscroll; + attribute EventHandler onscrollend; + +}; diff --git a/Userland/Libraries/LibWeb/DOM/Document.cpp b/Userland/Libraries/LibWeb/DOM/Document.cpp index 9bca97c566d..0d08d2e5685 100644 --- a/Userland/Libraries/LibWeb/DOM/Document.cpp +++ b/Userland/Libraries/LibWeb/DOM/Document.cpp @@ -21,6 +21,7 @@ #include #include #include +#include #include #include #include @@ -2807,6 +2808,13 @@ HTML::ListOfAvailableImages const& Document::list_of_available_images() const return *m_list_of_available_images; } +JS::NonnullGCPtr Document::visual_viewport() +{ + if (!m_visual_viewport) + m_visual_viewport = CSS::VisualViewport::create(*this).release_value_but_fixme_should_propagate_errors(); + return *m_visual_viewport; +} + void Document::register_intersection_observer(Badge, IntersectionObserver::IntersectionObserver& observer) { auto result = m_intersection_observers.set(observer); diff --git a/Userland/Libraries/LibWeb/DOM/Document.h b/Userland/Libraries/LibWeb/DOM/Document.h index 08c6c9086ca..c7a3d826bf2 100644 --- a/Userland/Libraries/LibWeb/DOM/Document.h +++ b/Userland/Libraries/LibWeb/DOM/Document.h @@ -380,6 +380,8 @@ public: void evaluate_media_queries_and_report_changes(); void add_media_query_list(JS::NonnullGCPtr); + JS::NonnullGCPtr visual_viewport(); + bool has_focus() const; void set_parser(Badge, HTML::HTMLParser&); @@ -668,6 +670,8 @@ private: // https://html.spec.whatwg.org/multipage/images.html#list-of-available-images OwnPtr m_list_of_available_images; + JS::GCPtr m_visual_viewport; + // NOTE: Not in the spec per say, but Document must be able to access all IntersectionObservers whose root is in the document. OrderedHashTable> m_intersection_observers; diff --git a/Userland/Libraries/LibWeb/Forward.h b/Userland/Libraries/LibWeb/Forward.h index 41c974b8dca..6d6ea84061d 100644 --- a/Userland/Libraries/LibWeb/Forward.h +++ b/Userland/Libraries/LibWeb/Forward.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020-2022, Andreas Kling + * Copyright (c) 2020-2023, Andreas Kling * Copyright (c) 2021, the SerenityOS developers. * * SPDX-License-Identifier: BSD-2-Clause @@ -181,6 +181,7 @@ class URLStyleValue; class UnicodeRange; class UnresolvedStyleValue; class UnsetStyleValue; +class VisualViewport; enum class MediaFeatureID; enum class PropertyID; diff --git a/Userland/Libraries/LibWeb/HTML/EventNames.h b/Userland/Libraries/LibWeb/HTML/EventNames.h index e646e3ff9ba..5bf8319519d 100644 --- a/Userland/Libraries/LibWeb/HTML/EventNames.h +++ b/Userland/Libraries/LibWeb/HTML/EventNames.h @@ -78,6 +78,7 @@ namespace Web::HTML::EventNames { __ENUMERATE_HTML_EVENT(reset) \ __ENUMERATE_HTML_EVENT(resize) \ __ENUMERATE_HTML_EVENT(scroll) \ + __ENUMERATE_HTML_EVENT(scrollend) \ __ENUMERATE_HTML_EVENT(securitypolicyviolation) \ __ENUMERATE_HTML_EVENT(seeked) \ __ENUMERATE_HTML_EVENT(seeking) \ diff --git a/Userland/Libraries/LibWeb/HTML/Window.cpp b/Userland/Libraries/LibWeb/HTML/Window.cpp index 9a28a1f57bc..37730c4e3db 100644 --- a/Userland/Libraries/LibWeb/HTML/Window.cpp +++ b/Userland/Libraries/LibWeb/HTML/Window.cpp @@ -1085,6 +1085,17 @@ WebIDL::ExceptionOr> Window::screen() return JS::NonnullGCPtr { *m_screen }; } +WebIDL::ExceptionOr> Window::visual_viewport() +{ + // If the associated document is fully active, the visualViewport attribute must return + // the VisualViewport object associated with the Window object’s associated document. + if (associated_document().is_fully_active()) + return associated_document().visual_viewport(); + + // Otherwise, it must return null. + return nullptr; +} + // https://w3c.github.io/csswg-drafts/cssom-view/#dom-window-innerwidth i32 Window::inner_width() const { diff --git a/Userland/Libraries/LibWeb/HTML/Window.h b/Userland/Libraries/LibWeb/HTML/Window.h index b0b2ec38064..9b0452165c2 100644 --- a/Userland/Libraries/LibWeb/HTML/Window.h +++ b/Userland/Libraries/LibWeb/HTML/Window.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020-2022, Andreas Kling + * Copyright (c) 2020-2023, Andreas Kling * Copyright (c) 2021-2023, Linus Groh * * SPDX-License-Identifier: BSD-2-Clause @@ -156,6 +156,7 @@ public: WebIDL::ExceptionOr> match_media(String const& query); WebIDL::ExceptionOr> screen(); + WebIDL::ExceptionOr> visual_viewport(); i32 inner_width() const; i32 inner_height() const; diff --git a/Userland/Libraries/LibWeb/HTML/Window.idl b/Userland/Libraries/LibWeb/HTML/Window.idl index 5537e5b6efd..4d7549cb7d8 100644 --- a/Userland/Libraries/LibWeb/HTML/Window.idl +++ b/Userland/Libraries/LibWeb/HTML/Window.idl @@ -1,6 +1,7 @@ #import #import #import +#import #import #import #import @@ -57,6 +58,7 @@ interface Window : EventTarget { // https://w3c.github.io/csswg-drafts/cssom-view/#extensions-to-the-window-interface [NewObject] MediaQueryList matchMedia(CSSOMString query); [SameObject, Replaceable] readonly attribute Screen screen; + [SameObject, Replaceable] readonly attribute VisualViewport? visualViewport; // viewport [Replaceable] readonly attribute long innerWidth; diff --git a/Userland/Libraries/LibWeb/idl_files.cmake b/Userland/Libraries/LibWeb/idl_files.cmake index 132235152cf..8578a2dd8cc 100644 --- a/Userland/Libraries/LibWeb/idl_files.cmake +++ b/Userland/Libraries/LibWeb/idl_files.cmake @@ -24,6 +24,7 @@ libweb_js_bindings(CSS/MediaQueryListEvent) libweb_js_bindings(CSS/Screen) libweb_js_bindings(CSS/StyleSheet) libweb_js_bindings(CSS/StyleSheetList) +libweb_js_bindings(CSS/VisualViewport) libweb_js_bindings(DOM/AbstractRange) libweb_js_bindings(DOM/Attr) libweb_js_bindings(DOM/AbortController)