1
Fork 0
mirror of https://github.com/RGBCube/serenity synced 2025-07-25 19:17:44 +00:00

LibWeb: Change viewport ownership from BrowsingContext to Navigable

This commit is contained in:
Aliaksandr Kalenik 2023-08-22 16:00:42 +02:00 committed by Andreas Kling
parent 4356d37b2c
commit dd7bba66ed
23 changed files with 196 additions and 129 deletions

View file

@ -540,73 +540,6 @@ void BrowsingContext::set_active_document(JS::NonnullGCPtr<DOM::Document> docume
previously_active_document->did_stop_being_active_document_in_browsing_context({});
}
void BrowsingContext::set_viewport_rect(CSSPixelRect const& rect)
{
bool did_change = false;
if (m_size != rect.size()) {
m_size = rect.size();
if (auto* document = active_document()) {
// NOTE: Resizing the viewport changes the reference value for viewport-relative CSS lengths.
document->invalidate_style();
document->set_needs_layout();
}
did_change = true;
}
if (m_viewport_scroll_offset != rect.location()) {
m_viewport_scroll_offset = rect.location();
scroll_offset_did_change();
did_change = true;
}
if (did_change && active_document()) {
active_document()->inform_all_viewport_clients_about_the_current_viewport_rect();
}
// Schedule the HTML event loop to ensure that a `resize` event gets fired.
HTML::main_thread_event_loop().schedule();
}
void BrowsingContext::set_size(CSSPixelSize size)
{
if (m_size == size)
return;
m_size = size;
if (auto* document = active_document()) {
document->invalidate_style();
document->set_needs_layout();
}
if (auto* document = active_document()) {
document->inform_all_viewport_clients_about_the_current_viewport_rect();
}
// Schedule the HTML event loop to ensure that a `resize` event gets fired.
HTML::main_thread_event_loop().schedule();
}
void BrowsingContext::set_needs_display()
{
set_needs_display(viewport_rect());
}
void BrowsingContext::set_needs_display(CSSPixelRect const& rect)
{
if (!viewport_rect().intersects(rect))
return;
if (is_top_level()) {
if (m_page)
m_page->client().page_did_invalidate(to_top_level_rect(rect));
return;
}
if (container() && container()->layout_node())
container()->layout_node()->set_needs_display();
}
void BrowsingContext::scroll_to(CSSPixelPoint position)
{
// NOTE: Scrolling to a position requires up-to-date layout *unless* we're scrolling to (0, 0)
@ -647,7 +580,8 @@ void BrowsingContext::scroll_to_anchor(DeprecatedString const& fragment)
auto& layout_node = *element->layout_node();
CSSPixelRect target_rect { layout_node.box_type_agnostic_position(), { viewport_rect().width(), viewport_rect().height() } };
auto const viewport_rect = document->viewport_rect();
CSSPixelRect target_rect { layout_node.box_type_agnostic_position(), { viewport_rect.width(), viewport_rect.height() } };
if (is<Layout::Box>(layout_node)) {
auto& layout_box = verify_cast<Layout::Box>(layout_node);
auto padding_box = layout_box.box_model().padding_box();

View file

@ -138,16 +138,6 @@ public:
Page* page() { return m_page; }
Page const* page() const { return m_page; }
CSSPixelSize size() const { return m_size; }
void set_size(CSSPixelSize);
void set_needs_display();
void set_needs_display(CSSPixelRect const&);
CSSPixelPoint viewport_scroll_offset() const { return m_viewport_scroll_offset; }
CSSPixelRect viewport_rect() const { return { m_viewport_scroll_offset, m_size }; }
void set_viewport_rect(CSSPixelRect const&);
FrameLoader& loader() { return m_loader; }
FrameLoader const& loader() const { return m_loader; }

View file

@ -25,6 +25,7 @@
#include <LibWeb/HTML/StructuredSerialize.h>
#include <LibWeb/HTML/TraversableNavigable.h>
#include <LibWeb/Infra/Strings.h>
#include <LibWeb/Layout/Node.h>
#include <LibWeb/Platform/EventLoopPlugin.h>
#include <LibWeb/XHR/FormData.h>
@ -1548,4 +1549,111 @@ void perform_url_and_history_update_steps(DOM::Document& document, AK::URL new_u
});
}
void Navigable::scroll_offset_did_change()
{
// https://w3c.github.io/csswg-drafts/cssom-view-1/#scrolling-events
// Whenever a viewport gets scrolled (whether in response to user interaction or by an API), the user agent must run these steps:
// 1. Let doc be the viewports associated Document.
auto doc = active_document();
VERIFY(doc);
// 2. If doc is already in docs pending scroll event targets, abort these steps.
for (auto& target : doc->pending_scroll_event_targets()) {
if (target.ptr() == doc)
return;
}
// 3. Append doc to docs pending scroll event targets.
doc->pending_scroll_event_targets().append(*doc);
}
CSSPixelRect Navigable::to_top_level_rect(CSSPixelRect const& a_rect)
{
auto rect = a_rect;
rect.set_location(to_top_level_position(a_rect.location()));
return rect;
}
CSSPixelPoint Navigable::to_top_level_position(CSSPixelPoint a_position)
{
auto position = a_position;
for (auto ancestor = parent(); ancestor; ancestor = ancestor->parent()) {
if (is<TraversableNavigable>(*ancestor))
break;
if (!ancestor->container())
return {};
if (!ancestor->container()->layout_node())
return {};
position.translate_by(ancestor->container()->layout_node()->box_type_agnostic_position());
}
return position;
}
void Navigable::set_viewport_rect(CSSPixelRect const& rect)
{
bool did_change = false;
if (m_size != rect.size()) {
m_size = rect.size();
if (auto document = active_document()) {
// NOTE: Resizing the viewport changes the reference value for viewport-relative CSS lengths.
document->invalidate_style();
document->set_needs_layout();
}
did_change = true;
}
if (m_viewport_scroll_offset != rect.location()) {
m_viewport_scroll_offset = rect.location();
scroll_offset_did_change();
did_change = true;
}
if (did_change && active_document()) {
active_document()->inform_all_viewport_clients_about_the_current_viewport_rect();
}
// Schedule the HTML event loop to ensure that a `resize` event gets fired.
HTML::main_thread_event_loop().schedule();
}
void Navigable::set_size(CSSPixelSize size)
{
if (m_size == size)
return;
m_size = size;
if (auto document = active_document()) {
document->invalidate_style();
document->set_needs_layout();
}
if (auto document = active_document()) {
document->inform_all_viewport_clients_about_the_current_viewport_rect();
}
// Schedule the HTML event loop to ensure that a `resize` event gets fired.
HTML::main_thread_event_loop().schedule();
}
void Navigable::set_needs_display()
{
set_needs_display(viewport_rect());
}
void Navigable::set_needs_display(CSSPixelRect const& rect)
{
if (!viewport_rect().intersects(rect))
return;
if (is<TraversableNavigable>(*this)) {
static_cast<TraversableNavigable*>(this)->page()->client().page_did_invalidate(to_top_level_rect(rect));
return;
}
if (container() && container()->layout_node())
container()->layout_node()->set_needs_display();
}
}

View file

@ -7,6 +7,7 @@
#pragma once
#include <AK/HashTable.h>
#include <AK/String.h>
#include <LibJS/Heap/Cell.h>
#include <LibWeb/Bindings/NavigationPrototype.h>
@ -19,6 +20,7 @@
#include <LibWeb/HTML/SourceSnapshotParams.h>
#include <LibWeb/HTML/StructuredSerialize.h>
#include <LibWeb/HTML/TokenizedFeatures.h>
#include <LibWeb/PixelUnits.h>
#include <LibWeb/XHR/FormDataEntry.h>
namespace Web::HTML {
@ -134,6 +136,19 @@ public:
[[nodiscard]] bool has_been_destroyed() const { return m_has_been_destroyed; }
void set_has_been_destroyed() { m_has_been_destroyed = true; }
CSSPixelPoint to_top_level_position(CSSPixelPoint);
CSSPixelRect to_top_level_rect(CSSPixelRect const&);
CSSPixelSize size() const { return m_size; }
void set_size(CSSPixelSize);
CSSPixelPoint viewport_scroll_offset() const { return m_viewport_scroll_offset; }
CSSPixelRect viewport_rect() const { return { m_viewport_scroll_offset, m_size }; }
void set_viewport_rect(CSSPixelRect const&);
void set_needs_display();
void set_needs_display(CSSPixelRect const&);
protected:
Navigable();
@ -146,6 +161,8 @@ private:
bool allowed_by_sandboxing_to_navigate(Navigable const& target, SourceSnapshotParams const&);
TargetSnapshotParams snapshot_target_snapshot_params();
void scroll_offset_did_change();
// https://html.spec.whatwg.org/multipage/document-sequences.html#nav-id
String m_id;
@ -168,6 +185,9 @@ private:
JS::GCPtr<NavigableContainer> m_container;
bool m_has_been_destroyed { false };
CSSPixelSize m_size;
CSSPixelPoint m_viewport_scroll_offset;
};
HashTable<Navigable*>& all_navigables();

View file

@ -46,6 +46,7 @@
#include <LibWeb/HTML/Scripting/ExceptionReporter.h>
#include <LibWeb/HTML/Storage.h>
#include <LibWeb/HTML/TokenizedFeatures.h>
#include <LibWeb/HTML/TraversableNavigable.h>
#include <LibWeb/HTML/Window.h>
#include <LibWeb/HTML/WindowProxy.h>
#include <LibWeb/HighResolutionTime/Performance.h>
@ -1101,8 +1102,8 @@ i32 Window::inner_width() const
{
// The innerWidth attribute must return the viewport width including the size of a rendered scroll bar (if any),
// or zero if there is no viewport.
if (auto const* browsing_context = associated_document().browsing_context())
return browsing_context->viewport_rect().width().to_int();
if (auto const navigable = associated_document().navigable())
return navigable->viewport_rect().width().to_int();
return 0;
}
@ -1111,8 +1112,8 @@ i32 Window::inner_height() const
{
// The innerHeight attribute must return the viewport height including the size of a rendered scroll bar (if any),
// or zero if there is no viewport.
if (auto const* browsing_context = associated_document().browsing_context())
return browsing_context->viewport_rect().height().to_int();
if (auto const navigable = associated_document().navigable())
return navigable->viewport_rect().height().to_int();
return 0;
}
@ -1122,7 +1123,7 @@ double Window::scroll_x() const
// The scrollX attribute must return the x-coordinate, relative to the initial containing block origin,
// of the left of the viewport, or zero if there is no viewport.
if (auto* page = this->page())
return page->top_level_browsing_context().viewport_scroll_offset().x().to_double();
return page->top_level_traversable()->viewport_scroll_offset().x().to_double();
return 0;
}
@ -1132,7 +1133,7 @@ double Window::scroll_y() const
// The scrollY attribute must return the y-coordinate, relative to the initial containing block origin,
// of the top of the viewport, or zero if there is no viewport.
if (auto* page = this->page())
return page->top_level_browsing_context().viewport_scroll_offset().y().to_double();
return page->top_level_traversable()->viewport_scroll_offset().y().to_double();
return 0;
}
@ -1158,12 +1159,12 @@ void Window::scroll(ScrollToOptions const& options)
auto* page = this->page();
if (!page)
return;
auto const& top_level_browsing_context = page->top_level_browsing_context();
auto top_level_traversable = page->top_level_traversable();
// 1. If invoked with one argument, follow these substeps:
// 1. Let options be the argument.
auto viewport_rect = top_level_browsing_context.viewport_rect().to_type<float>();
auto viewport_rect = top_level_traversable->viewport_rect().to_type<float>();
// 2. Let x be the value of the left dictionary member of options, if present, or the viewports current scroll
// position on the x axis otherwise.
@ -1206,7 +1207,7 @@ void Window::scroll(ScrollToOptions const& options)
// smooth scroll, abort these steps.
// 11. Let document be the viewports associated Document.
auto const* document = top_level_browsing_context.active_document();
auto const document = top_level_traversable->active_document();
// 12. Perform a scroll of the viewport to position, documents root element as the associated element, if there is
// one, or null otherwise, and the scroll behavior being the value of the behavior dictionary member of options.