Skip to content

Commit 440413e

Browse files
Psychpsyopbrw
authored andcommitted
LibWeb: Implement some viewport proximity features
1 parent 556555c commit 440413e

File tree

3 files changed

+119
-3
lines changed

3 files changed

+119
-3
lines changed

Libraries/LibWeb/DOM/Element.cpp

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,7 @@
6767
#include <LibWeb/Page/Page.h>
6868
#include <LibWeb/Painting/PaintableBox.h>
6969
#include <LibWeb/Painting/ViewportPaintable.h>
70+
#include <LibWeb/Selection/Selection.h>
7071
#include <LibWeb/WebIDL/AbstractOperations.h>
7172
#include <LibWeb/WebIDL/DOMException.h>
7273
#include <LibWeb/WebIDL/ExceptionOr.h>
@@ -2599,6 +2600,73 @@ bool Element::check_visibility(Optional<CheckVisibilityOptions> options)
25992600
return true;
26002601
}
26012602

2603+
// https://drafts.csswg.org/css-contain/#proximity-to-the-viewport
2604+
void Element::determine_proximity_to_the_viewport()
2605+
{
2606+
// An element that has content-visibility: auto is in one of three states when it comes to its proximity to the viewport:
2607+
2608+
// - The element is close to the viewport: In this state, the element is considered "on-screen": its paint
2609+
// containment box's overflow clip edge intersects with the viewport, or a user-agent defined margin around the
2610+
// viewport.
2611+
auto viewport_rect = document().viewport_rect();
2612+
// NOTE: This margin is meant to allow the user agent to begin preparing for an element to be in the
2613+
// viewport soon. A margin of 50% is suggested as a reasonable default.
2614+
viewport_rect.inflate(viewport_rect.width(), viewport_rect.height());
2615+
// FIXME: We don't have paint containment or the overflow clip edge yet, so this is just using the absolute rect for now.
2616+
if (paintable_box()->absolute_rect().intersects(viewport_rect))
2617+
m_proximity_to_the_viewport = ProximityToTheViewport::CloseToTheViewport;
2618+
2619+
// FIXME: If a filter (see [FILTER-EFFECTS-1]) with non local effects includes the element as part of its input, the user
2620+
// agent should also treat the element as relevant to the user when the filter’s output can affect the rendering
2621+
// within the viewport (or within the user-agent defined margin around the viewport), even if the element itself is
2622+
// still off-screen.
2623+
2624+
// - The element is far away from the viewport: In this state, the element’s proximity to the viewport has been
2625+
// computed and is not close to the viewport.
2626+
m_proximity_to_the_viewport = ProximityToTheViewport::FarAwayFromTheViewport;
2627+
2628+
// - The element’s proximity to the viewport is not determined: In this state, the computation to determine the
2629+
// element’s proximity to the viewport has not been done since the last time the element was connected.
2630+
// NOTE: This function is what does the computation to determine the element’s proximity to the viewport, so this is not the case.
2631+
}
2632+
2633+
// https://drafts.csswg.org/css-contain/#relevant-to-the-user
2634+
bool Element::is_relevant_to_the_user()
2635+
{
2636+
// An element is relevant to the user if any of the following conditions are true:
2637+
2638+
// The element is close to the viewport.
2639+
if (m_proximity_to_the_viewport == ProximityToTheViewport::CloseToTheViewport)
2640+
return true;
2641+
2642+
// Either the element or its contents are focused, as described in the focus section of the HTML spec.
2643+
auto* focused_element = document().focused_element();
2644+
if (focused_element && is_inclusive_ancestor_of(*focused_element))
2645+
return true;
2646+
2647+
// Either the element or its contents are selected, where selection is described in the selection API.
2648+
if (document().get_selection()->contains_node(*this, true))
2649+
return true;
2650+
2651+
// Either the element or its contents are placed in the top layer.
2652+
bool is_in_top_layer = false;
2653+
for_each_in_inclusive_subtree_of_type<Element>([&](auto& element) {
2654+
if (element.in_top_layer()) {
2655+
is_in_top_layer = true;
2656+
return TraversalDecision::Break;
2657+
}
2658+
2659+
return TraversalDecision::Continue;
2660+
});
2661+
if (is_in_top_layer)
2662+
return true;
2663+
2664+
// FIXME: The element has a flat tree descendant that is captured in a view transition.
2665+
2666+
// NOTE: none of the above conditions are true, so the element is not relevant to the user.
2667+
return false;
2668+
}
2669+
26022670
bool Element::id_reference_exists(String const& id_reference) const
26032671
{
26042672
return document().get_element_by_id(id_reference);

Libraries/LibWeb/DOM/Element.h

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,17 @@ enum class CustomElementState {
8585
Custom,
8686
};
8787

88+
// https://drafts.csswg.org/css-contain/#proximity-to-the-viewport
89+
// An element that has content-visibility: auto is in one of three states when it comes to its proximity to the viewport:
90+
enum class ProximityToTheViewport {
91+
// - The element is close to the viewport:
92+
CloseToTheViewport,
93+
// - The element is far away from the viewport:
94+
FarAwayFromTheViewport,
95+
// - The element’s proximity to the viewport is not determined:
96+
NotDetermined,
97+
};
98+
8899
class Element
89100
: public ParentNode
90101
, public ChildNode<Element>
@@ -377,6 +388,10 @@ class Element
377388
void resolve_counters(CSS::ComputedProperties&);
378389
void inherit_counters();
379390

391+
ProximityToTheViewport proximity_to_the_viewport() const { return m_proximity_to_the_viewport; }
392+
void determine_proximity_to_the_viewport();
393+
bool is_relevant_to_the_user();
394+
380395
protected:
381396
Element(Document&, DOM::QualifiedName);
382397
virtual void initialize(JS::Realm&) override;
@@ -469,6 +484,9 @@ class Element
469484
OwnPtr<CSS::CountersSet> m_counters_set;
470485

471486
GC::Ptr<DOM::Element> m_aria_active_descendant_element;
487+
488+
// https://drafts.csswg.org/css-contain/#proximity-to-the-viewport
489+
ProximityToTheViewport m_proximity_to_the_viewport { ProximityToTheViewport::NotDetermined };
472490
};
473491

474492
template<>

Libraries/LibWeb/HTML/EventLoop/EventLoop.cpp

Lines changed: 33 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
#include <LibWeb/CSS/FontFaceSet.h>
1212
#include <LibWeb/CSS/StyleComputer.h>
1313
#include <LibWeb/DOM/Document.h>
14+
#include <LibWeb/DOM/Element.h>
1415
#include <LibWeb/HTML/BrowsingContext.h>
1516
#include <LibWeb/HTML/EventLoop/EventLoop.h>
1617
#include <LibWeb/HTML/Scripting/Environments.h>
@@ -20,6 +21,7 @@
2021
#include <LibWeb/HighResolutionTime/Performance.h>
2122
#include <LibWeb/HighResolutionTime/TimeOrigin.h>
2223
#include <LibWeb/Page/Page.h>
24+
#include <LibWeb/Painting/PaintableBox.h>
2325
#include <LibWeb/Platform/EventLoopPlugin.h>
2426
#include <LibWeb/Platform/Timer.h>
2527

@@ -249,6 +251,7 @@ void EventLoop::queue_task_to_update_the_rendering()
249251
}
250252
}
251253

254+
// https://html.spec.whatwg.org/#update-the-rendering
252255
void EventLoop::update_the_rendering()
253256
{
254257
VERIFY(!m_is_running_rendering_task);
@@ -335,9 +338,36 @@ void EventLoop::update_the_rendering()
335338
// NOTE: Recalculation of styles is handled by update_layout()
336339
document->update_layout();
337340

338-
// FIXME: 2. Let hadInitialVisibleContentVisibilityDetermination be false.
339-
// FIXME: 3. For each element element with 'auto' used value of 'content-visibility':
340-
// FIXME: 4. If hadInitialVisibleContentVisibilityDetermination is true, then continue.
341+
// 2. Let hadInitialVisibleContentVisibilityDetermination be false.
342+
bool had_initial_visible_content_visibility_determination = false;
343+
344+
// 3. For each element element with 'auto' used value of 'content-visibility':
345+
auto* document_element = document->document_element();
346+
if (document_element) {
347+
document_element->for_each_in_inclusive_subtree_of_type<Web::DOM::Element>([&](auto& element) {
348+
auto const& paintable_box = element.paintable_box();
349+
if (!paintable_box || paintable_box->computed_values().content_visibility() != CSS::ContentVisibility::Auto) {
350+
return TraversalDecision::Continue;
351+
}
352+
353+
// 1. Let checkForInitialDetermination be true if element's proximity to the viewport is not determined and it is not relevant to the user. Otherwise, let checkForInitialDetermination be false.
354+
bool check_for_initial_determination = element.proximity_to_the_viewport() == Web::DOM::ProximityToTheViewport::NotDetermined && !element.is_relevant_to_the_user();
355+
356+
// 2. Determine proximity to the viewport for element.
357+
element.determine_proximity_to_the_viewport();
358+
359+
// 3. If checkForInitialDetermination is true and element is now relevant to the user, then set hadInitialVisibleContentVisibilityDetermination to true.
360+
if (check_for_initial_determination && element.is_relevant_to_the_user()) {
361+
had_initial_visible_content_visibility_determination = true;
362+
}
363+
364+
return TraversalDecision::Continue;
365+
});
366+
}
367+
368+
// 4. If hadInitialVisibleContentVisibilityDetermination is true, then continue.
369+
if (had_initial_visible_content_visibility_determination)
370+
continue;
341371

342372
// 5. Gather active resize observations at depth resizeObserverDepth for doc.
343373
document->gather_active_observations_at_depth(resize_observer_depth);

0 commit comments

Comments
 (0)