Skip to content

Commit

Permalink
LibWeb: Allow inline nodes to establish a stacking context
Browse files Browse the repository at this point in the history
With this change, a stacking context can be established by any
paintable, including inline paintables. The stacking context traversal
is updated to remove the assumption that the stacking context root is
paintable box.
  • Loading branch information
kalenikaliaksandr committed Jan 5, 2024
1 parent 1919b61 commit e51f043
Show file tree
Hide file tree
Showing 12 changed files with 243 additions and 139 deletions.
16 changes: 16 additions & 0 deletions Tests/LibWeb/Ref/inline-stacking-context.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
<link rel="match" href="reference/inline-stacking-context-ref.html" /><style>
span {
z-index: 10;
background: orange;
position: relative;
opacity: 0.5;
}
div {
z-index: 5;
width: 50px;
height: 50px;
background: green;
position: relative;
top: -10px;
}
</style><span>hello</span><div></div>
17 changes: 17 additions & 0 deletions Tests/LibWeb/Ref/reference/inline-stacking-context-ref.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
<style>
#text {
z-index: 10;
background: orange;
position: relative;
opacity: 0.5;
width: fit-content;
}
#box {
z-index: 5;
width: 50px;
height: 50px;
background: green;
position: relative;
top: -10px;
}
</style><div id="text">hello</div><div id="box"></div>
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
hello true
23 changes: 23 additions & 0 deletions Tests/LibWeb/Text/input/hit_testing/inline-stacking-context.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
<style>
span {
z-index: 10;
background: orange;
position: relative;
opacity: 0.5;
font-size: 100px;
}
div {
z-index: 5;
width: 100px;
height: 100px;
background: green;
position: relative;
top: -10px;
}
</style><span id="inline-stacking-context">hello</span><div></div>
<script src="../include.js"></script>
<script>
test(() => {
println(internals.hitTest(50, 50).node.parentNode === document.getElementById("inline-stacking-context"));
});
</script>
2 changes: 2 additions & 0 deletions Userland/Libraries/LibWeb/Painting/InlinePaintable.h
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@ class InlinePaintable final : public Paintable {

CSSPixelRect bounding_rect() const;

virtual bool is_inline_paintable() const override { return true; }

void mark_contained_fragments();

private:
Expand Down
44 changes: 40 additions & 4 deletions Userland/Libraries/LibWeb/Painting/Paintable.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
#include <LibWeb/Layout/BlockContainer.h>
#include <LibWeb/Painting/Paintable.h>
#include <LibWeb/Painting/PaintableBox.h>
#include <LibWeb/Painting/StackingContext.h>

namespace Web::Painting {

Expand All @@ -17,6 +18,10 @@ Paintable::Paintable(Layout::Node const& layout_node)
{
}

Paintable::~Paintable()
{
}

void Paintable::visit_edges(Cell::Visitor& visitor)
{
Base::visit_edges(visitor);
Expand All @@ -28,6 +33,11 @@ void Paintable::visit_edges(Cell::Visitor& visitor)
visitor.visit(m_containing_block.value());
}

bool Paintable::is_visible() const
{
return computed_values().visibility() == CSS::Visibility::Visible && computed_values().opacity() != 0;
}

bool Paintable::is_positioned() const
{
if (layout_node().is_grid_item() && computed_values().z_index().has_value()) {
Expand Down Expand Up @@ -88,11 +98,37 @@ Optional<HitTestResult> Paintable::hit_test(CSSPixelPoint, HitTestType) const
return {};
}

StackingContext const* Paintable::stacking_context_rooted_here() const
StackingContext* Paintable::enclosing_stacking_context()
{
if (!is<PaintableBox>(*this))
return nullptr;
return static_cast<PaintableBox const&>(*this).stacking_context();
for (auto* ancestor = parent(); ancestor; ancestor = ancestor->parent()) {
if (auto* stacking_context = ancestor->stacking_context())
return const_cast<StackingContext*>(stacking_context);
}
// We should always reach the viewport's stacking context.
VERIFY_NOT_REACHED();
}

void Paintable::set_stacking_context(NonnullOwnPtr<StackingContext> stacking_context)
{
m_stacking_context = move(stacking_context);
}

void Paintable::invalidate_stacking_context()
{
m_stacking_context = nullptr;
}

PaintableBox const* Paintable::nearest_scrollable_ancestor_within_stacking_context() const
{
auto* ancestor = parent();
while (ancestor) {
if (ancestor->stacking_context())
return nullptr;
if (ancestor->is_paintable_box() && static_cast<PaintableBox const*>(ancestor)->has_scrollable_overflow())
return static_cast<PaintableBox const*>(ancestor);
ancestor = ancestor->parent();
}
return nullptr;
}

}
18 changes: 16 additions & 2 deletions Userland/Libraries/LibWeb/Painting/Paintable.h
Original file line number Diff line number Diff line change
Expand Up @@ -54,8 +54,9 @@ class Paintable
JS_CELL(Paintable, Cell);

public:
virtual ~Paintable() = default;
virtual ~Paintable();

[[nodiscard]] bool is_visible() const;
[[nodiscard]] bool is_positioned() const;
[[nodiscard]] bool is_fixed_position() const { return layout_node().is_fixed_position(); }
[[nodiscard]] bool is_absolutely_positioned() const { return layout_node().is_absolutely_positioned(); }
Expand Down Expand Up @@ -109,6 +110,13 @@ class Paintable
return TraversalDecision::Continue;
}

StackingContext* stacking_context() { return m_stacking_context; }
StackingContext const* stacking_context() const { return m_stacking_context; }
void set_stacking_context(NonnullOwnPtr<StackingContext>);
StackingContext* enclosing_stacking_context();

void invalidate_stacking_context();

virtual void before_paint(PaintContext&, PaintPhase) const { }
virtual void after_paint(PaintContext&, PaintPhase) const { }

Expand Down Expand Up @@ -171,8 +179,12 @@ class Paintable

[[nodiscard]] virtual bool is_paintable_box() const { return false; }
[[nodiscard]] virtual bool is_paintable_with_lines() const { return false; }
[[nodiscard]] virtual bool is_inline_paintable() const { return false; }

StackingContext const* stacking_context_rooted_here() const;
DOM::Document const& document() const { return layout_node().document(); }
DOM::Document& document() { return layout_node().document(); }

PaintableBox const* nearest_scrollable_ancestor_within_stacking_context() const;

protected:
explicit Paintable(Layout::Node const&);
Expand All @@ -184,6 +196,8 @@ class Paintable
JS::NonnullGCPtr<Layout::Node const> m_layout_node;
JS::NonnullGCPtr<HTML::BrowsingContext> m_browsing_context;
Optional<JS::GCPtr<Layout::Box const>> mutable m_containing_block;

OwnPtr<StackingContext> m_stacking_context;
};

inline DOM::Node* HitTestResult::dom_node()
Expand Down
38 changes: 0 additions & 38 deletions Userland/Libraries/LibWeb/Painting/PaintableBox.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -40,16 +40,6 @@ PaintableBox::~PaintableBox()
{
}

bool PaintableBox::is_visible() const
{
return computed_values().visibility() == CSS::Visibility::Visible && computed_values().opacity() != 0;
}

void PaintableBox::invalidate_stacking_context()
{
m_stacking_context = nullptr;
}

PaintableWithLines::PaintableWithLines(Layout::BlockContainer const& layout_box)
: PaintableBox(layout_box)
{
Expand Down Expand Up @@ -165,16 +155,6 @@ CSSPixelRect PaintableBox::absolute_paint_rect() const
return *m_absolute_paint_rect;
}

StackingContext* PaintableBox::enclosing_stacking_context()
{
for (auto* ancestor = parent(); ancestor; ancestor = ancestor->parent()) {
if (auto* stacking_context = ancestor->stacking_context_rooted_here())
return const_cast<StackingContext*>(stacking_context);
}
// We should always reach the viewport's stacking context.
VERIFY_NOT_REACHED();
}

Optional<CSSPixelRect> PaintableBox::get_clip_rect() const
{
auto clip = computed_values().clip();
Expand Down Expand Up @@ -748,11 +728,6 @@ Layout::BlockContainer& PaintableWithLines::layout_box()
return static_cast<Layout::BlockContainer&>(PaintableBox::layout_box());
}

void PaintableBox::set_stacking_context(NonnullOwnPtr<StackingContext> stacking_context)
{
m_stacking_context = move(stacking_context);
}

Optional<HitTestResult> PaintableBox::hit_test(CSSPixelPoint position, HitTestType type) const
{
if (!is_visible())
Expand Down Expand Up @@ -828,17 +803,4 @@ Optional<HitTestResult> PaintableWithLines::hit_test(CSSPixelPoint position, Hit
return {};
}

PaintableBox const* PaintableBox::nearest_scrollable_ancestor_within_stacking_context() const
{
auto* ancestor = parent();
while (ancestor) {
if (ancestor->stacking_context_rooted_here())
return nullptr;
if (ancestor->is_paintable_box() && static_cast<PaintableBox const*>(ancestor)->has_scrollable_overflow())
return static_cast<PaintableBox const*>(ancestor);
ancestor = ancestor->parent();
}
return nullptr;
}

}
16 changes: 0 additions & 16 deletions Userland/Libraries/LibWeb/Painting/PaintableBox.h
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,6 @@ class PaintableBox : public Paintable {

virtual void paint(PaintContext&, PaintPhase) const override;

[[nodiscard]] bool is_visible() const;

virtual Optional<CSSPixelRect> get_masking_area() const { return {}; }
virtual Optional<Gfx::Bitmap::MaskKind> get_mask_type() const { return {}; }
virtual RefPtr<Gfx::Bitmap> calculate_mask(PaintContext&, CSSPixelRect const&) const { return {}; }
Expand Down Expand Up @@ -122,17 +120,9 @@ class PaintableBox : public Paintable {

void set_overflow_data(OverflowData data) { m_overflow_data = move(data); }

StackingContext* stacking_context() { return m_stacking_context; }
StackingContext const* stacking_context() const { return m_stacking_context; }
void set_stacking_context(NonnullOwnPtr<StackingContext>);
StackingContext* enclosing_stacking_context();

DOM::Node const* dom_node() const { return layout_box().dom_node(); }
DOM::Node* dom_node() { return layout_box().dom_node(); }

DOM::Document const& document() const { return layout_box().document(); }
DOM::Document& document() { return layout_box().document(); }

virtual void apply_scroll_offset(PaintContext&, PaintPhase) const override;
virtual void reset_scroll_offset(PaintContext&, PaintPhase) const override;

Expand All @@ -143,8 +133,6 @@ class PaintableBox : public Paintable {

virtual bool handle_mousewheel(Badge<EventHandler>, CSSPixelPoint, unsigned buttons, unsigned modifiers, int wheel_delta_x, int wheel_delta_y) override;

void invalidate_stacking_context();

enum class ConflictingElementKind {
Cell,
Row,
Expand Down Expand Up @@ -194,8 +182,6 @@ class PaintableBox : public Paintable {
void set_box_shadow_data(Vector<ShadowData> box_shadow_data) { m_box_shadow_data = move(box_shadow_data); }
Vector<ShadowData> const& box_shadow_data() const { return m_box_shadow_data; }

PaintableBox const* nearest_scrollable_ancestor_within_stacking_context() const;

protected:
explicit PaintableBox(Layout::Box const&);

Expand All @@ -217,8 +203,6 @@ class PaintableBox : public Paintable {
CSSPixelPoint m_offset;
CSSPixelSize m_content_size;

OwnPtr<StackingContext> m_stacking_context;

Optional<CSSPixelRect> mutable m_absolute_rect;
Optional<CSSPixelRect> mutable m_absolute_paint_rect;

Expand Down
Loading

0 comments on commit e51f043

Please sign in to comment.