Previously, we would hit test positioned elements, then stacking
contexts with z-index 0, as two seperate steps. This did not really
follow the reverse paint order, where positioned elements and stacking
contexts with z-index 0 are painted during the same tree transversal.
This commit updates
for_each_in_subtree_of_type_within_same_stacking_context_in_reverse()
to return the stacking contexts it comes across too, but not recurse
into them. This more closely follows the paint order.
This fixes examples such as:
<div id="a" style="width: 10px; height: 10px">
<div id="b" style="position: absolute; width: 10px; height: 10px">
<div
style="position: absolute; width: 10px; height: 10px; z-index: 0"
>
<div id="c"
style="width: 100%; height: 100%; background-color:red;"
onclick="alert('You Win!')">
</div>
</div>
</div>
</div>
Where previously the onclick on #c would never fire as hit testing
always stopped at #b. This is reduced from Google Street View,
which becomes interactable after this commit.
...and replace it with AngleOrCalculated.
This has the nice bonus effect of actually handling `calc()` for angles
in a transform function. :^) (Previously we just would have asserted.)
The name "initial containing block" was wrong for this, as it doesn't
correspond to the HTML element, and that's specifically what it's
supposed to do! :^)
This simplifies the ownership model between DOM/layout/paint nodes
immensely by deferring to the garbage collector for figuring out what's
live and what's not.
This fixes a few sizing issues too. The page size is now correct in most
cases! \o/
We get to remove some of the `to_type<>()` shenanigans, though it
reappears in some other places.
...and also for hit testing, which is involved in most of them.
Much of this is temporary conversions and other awkwardness, which
should resolve itself as the rest of LibWeb is converted to these new
types. Hopefully. :thousandyakstare:
Discord modals/pop-outs are in a "layerContainer" <div> with
`z-index: 1002`, which then has an immediate child <div> called
"positionLayer" with `z-index: 0`. We only ever hit test child stacking
contexts with z-index set to anything but 0 (step 7 and step 1 of the
hit test), but not for exactly 0 (step 6). This made it impossible to
hit any element inside positionLayer, making pop-ups such as the emojis
and GIFs unusable.
This will make it easier to support both string types at the same time
while we convert code, and tracking down remaining uses.
One big exception is Value::to_string() in LibJS, where the name is
dictated by the ToString AO.
Since handling overflow: hidden in PaintableBox::before_children_paint
while following paint traversal order can't result in correctly computed
clip rectangle for elements that create their own stacking context
(because before_children_paint is called only for parent but overflow:
hidden can be set somewhere deeper but not in direct ancestor), here
introduced new function PaintableBox::clip_rect() that computes clip
rectangle by looking into containing block.
should_clip_overflow flag that disables clip for absolutely positioned
elements in before_children_paint and after_children_paint is removed
because after changing clip rectangle to be computed from not parent
but containing block it is not needed anymore (absolutely positioned
item is clipped if it's containing block has hidden overflow)
We were doing a forward traversal in hit testing which led to sometimes
incorrect results when multiple boxes were occupying the same X and Y
coordinate.
Positioned descendants are now handled entirely by paint_internal()
so we can just skip over positioned children in paint_descendants().
This avoids drawing the same boxes multiple times.
This "worked" before because all positioned elements would create their
own stacking context. When we stopped doing this, there was nobody to
actually paint positioned descendants with `z-index: auto`.
This patch splits up steps 8 and 9 of the paint order algorithm and
implements step 8 as a paint tree traversal. There's more to step 8 than
I've implemented here, so I've left a FIXME for our future selves.
Since positioned elements no longer automatically create stacking
contexts, we can't rely on this assumption when painting descendants of
a stacking context.
In this commit, we fix an issue that manifested as a failure to
Gfx::Painter::restore() in the "Overlay" paint phase. What happened was
that a CSS clip was being applied in the "Background" paint phase, and
then unapplied in the "Overlay" phase. Due to bogus checks in
paint_descendants(), the "Background" phase never ran for positioned
elements, but the "Overlay" phase did.
The check for positioned elements was bogus in the first place and had
never actually worked before, since we would always skip over positioned
descendants due to them having stacking contexts.
We're supposed to hit test positive z-index stacking contexts first,
and negative z-index stacking contexts later. Instead, we were hit
testing all stacking contexts both times.
This made hit testing unbearably slow on some websites.
While we're here, also add an extra comment about why stacking contexts
are traversed in reverse order. It tripped me up while looking at this,
so I'm sure it could trip someone else up too.
Regressed in 44057c9482.
If the 2D transform in effect is just a simple translation, we don't
need to draw into a temporary bitmap and then transform it. We can
just translate the painter. :^)
This fixes an edge case, where the destination rect falls partly
outside the painter, so is clipped to a smaller size in
`get_region_bitmap()` (which needs to be accounted for with an extra
offset).
This now copies the area under the destination to a new bitmap, that
is then scaled to the size of the source. The element is then painted
into that bitmap, which is then scaled and painted back to
the destination. This is done as many effects such as shadows, border
radii, filters, etc require being able to read pixels from the painter.
This does work (and is not that noticeable in many cases), but it does
mean there may be a few scaling artifacts in the background
around transformed elements. Though that was already the case before
anyway for the elements (since it is just a bitmap scale).
What we really want is to (where possible) just scale the paintable
and its descendants, then paint things normally, which would give
much nicer results (but is much more tricky to achieve).
This also now makes it so only a bitmap of the size of the paintable is
copied/created, rather than the whole page.
When mousing over twitter, 17% of time was spent computing stacking
context transform origins. Since this never changes after the stacking
context is created, we can cache it and avoid all that work.
63c727a was meant to stop clipping absolutely positioned descendants,
but used `is_positioned()` rather than `is_absolutely_positioned()`,
which meant it disabled clipping in many more cases that it should
have.
This is mainly so we can easily read that matrix later, but also has the
benefit of only calculating the matrix once, instead of every time we
paint. :^)
Also, made the `reference_length` parameter optional for the lambda that
extracts transform-function parameters, since it is only needed to
resolve `LengthPercentage` parameters.
This now calls before/after_child_paint() on the parent paintable
of a positioned child. This allows the parent's overflow clipping
to apply to the child.
Previously, before/after_children_paint() was only called for the
"Foreground" paint phase, this meant the backgrounds and other
features of child nodes of a element with overflow: hidden were
not clipped.
Each of these strings would previously rely on StringView's char const*
constructor overload, which would call __builtin_strlen on the string.
Since we now have operator ""sv, we can replace these with much simpler
versions. This opens the door to being able to remove
StringView(char const*).
No functional changes.
When cloning the PaintContext we should be using the painter backed by
the bitmap created for this stacking context layer.
Fixes: 54c3053bc3 ("LibWeb: Preserve paint state when painting...")