Taking a ColorResolutionContext directly instead of creating one from a
layout node allows us to call this from places where we don't have a
layout node.
Using a generic context argument will allow us to resolve colors in
places where we have all the required information but not in the form of
a layout node as was expected previously.
`CSSColorValue`s which have unresolved `calc` components should be able
to be resolved. Previously we would always resolve them but with
incorrect values.
This is useful as we will now be able to now whether we should serialize
colors in their normalized form or not.
Slight regression in that we now serialize (RGB, HSL and HWB) colors
with components that rely on compute-time information as an empty
string, but that will be fixed in the next commit.
The existing resolve methods are not to spec and we are working to
replace them with new ones based on the `simplify_a_calculation_tree`
method.
These are marked as deprecated rather than replaced outright as work
will need to be done on the caller side to be made compatible with the
new methods, for instance the new methods can fail to resolve (e.g.
if we are missing required context), where the existing methods will
always resolve (albeit sometimes with an incorrect value).
No functionality changes.
This fixes an issue where we'd make an absolute mess from nested SVG
roots with display:block. Before this fix, the inner SVG root would
trigger the inline continuation logic and try to split the tree.
83b6bc4 went too far by forbidding SVGSVGElement from establishing a
stacking context. This element type does follow the behavior of CSS
boxes, unlike inner SVG elements like `<rect>`, `<circle>`, etc., which
are not supposed to be aware of concepts like stacking contexts,
overflow clipping, scroll offsets, etc.
This change allows us to delete overrides of `before_paint()` and
`after_paint()` in SVGPaintable and SVGSVGPaintable, because display
list recording code has been rearranged to take care of clipping and
scrolling before recursing into SVGSVGPaintable descendants.
`Screenshot/images/css-transform-box-ref.png` expectation is updated and
fixes a bug where a rectangle at the very bottom of the page was not
clipped correctly.
`Screenshot/images/svg-filters-lb-website-ref.png` has a more subtle
difference, but if you look closely, you’ll see it matches other
browsers more closely now.
We assume elsewhere that any abspos element's containing block must be
some kind of Layout::Box, so let's enforce that when deciding if a box
can be such a container.
This fixes a bad downcast on https://serpapi.com/
This exposed a few bugs which caused the following tests to behave
incorrectly:
- `tab-size-text-wrap.html`: This previously relied on a bug where we
incorrectly treated `white-space: pre` as allowing text wrapping. The
fix here is to implement the text-wrap CSS shorthand property.
- `execCommand-preserveWhitespace.html`: We don't correctly serialize
shorthand properties. This is covered by an existing FIXME in
`CSSStyleProperties::serialized()`
- `white-space-shorthand.html`: The last 5 subtests here fail as we
don't correctly handle shorthand properties in
`CSSStyleProperties::remove_property()`. This is covered by an
existing FIXME in said function.
When restructuring a block node inside an inline parent, if the
nearest block ancestor is `display: inline-block`, ensure that
the generated anonymous wrappers also have `display: inline-block`.
This fixes layout issues with block elements nested
inside inline-block elements.
The special case for anonymous table wrappers actually ended up hurting
correctness by preventing the full ancestor chain from being marked for
for intrinsic size cache invalidation.
Caused Layout/input/table/propagate-style-update-to-wrapper.html to
flake on CI, and was easy to reproduce locally with sanitizers.
The fix here is simply to remove the special handling of anonymous table
wrapper parents, since *all* parents are invalidated automatically
anyway!
Began flaking in fa9c463ffd.
Instead of indiscriminately clearing the cache for all anonymous boxes,
we now only clear it for those that were generated by a non-anonymous
box in need of layout update.
This increases the cache hit rate and allows us to avoid more work.
We now cache the containing block (box) once at the start of layout,
which allows Layout::Node::containing_block() to return instantly
instead of doing tree traversal.
Removes a 0.7% profile item on Speedometer 3.
This is a improved version of a73cd88f0c
The old commit was reverted in 552dd18696
The new version only paints an element into a new layer if background
blend modes other than normal are used. The rasterization performance
of most websites should therefore not suffer.
Co-Authored-By: Alexander Kalenik <kalenik.aliaksandr@gmail.com>
This reverts commit a73cd88f0c.
Emitting SaveLayer for each paintable made rasterization a lot slower
on every website because now Skia has to allocate enormous amounts of
temporary surfaces. Let's revert it for now and figure how to implement
it with less aggressive SaveLayer usage.
positioned element is a descendant of inline-block
Sets inline block offsets in InlineFormattingContext.cpp, but this is
not enough. When static position rect is calculated during layout,
not all ancestors of abspos box may have their offsets calculated yet
(more info here: https://github.com/LadybirdBrowser/ladybird/pull/2583#issuecomment-2507140272).
So now static position rect is calculated relative to static containing
block during layout and calculation relative to actual containing block
is done later in
FormattingContext::layout_absolutely_positioned_element.
Fixes wpt/css/CSS2/abspos/static-inside-inline-block.html
Before this change, an element masked with 'mask-image: url(...)' would
show the mask, but 'mask: url(...)' would not. On e.g. dialogic.nl it
would show white boxes instead of the actual images in the top
navigation bar. We still do not support many of the other mask
properties, but with this change at least the masks show up in both
cases.
This reduces the number of `.cpp` files that need to be recompiled when
one of the below header files changes as follows:
CSS/ComputedProperties.h: 1113 -> 49
CSS/ComputedValues.h: 1120 -> 209
Having one big `if` to determine whether or not we should restructure an
inline layout node became a bit unwieldy. This extracts the logic into a
separate method.
When drawing a table, some of the CSS properties must be moved from the
table grid box to an anonamyous table wrapper box. One of these
properties is `position`. `z-index` however is not. This leads to the
following behavior if a table has both `position` and `z-index`:
* The wrapper box has the `position`, but a `z-index` of `auto`.
* The grid box has the `z-index`, but `position: static`.
This effectively means that the `z-index property is ignored since it
has no effect on non-positioned elements. This behavior contradicts what
other browsers do and causes layout issues on websites.
To align Ladybird behavior with other browser this commit also moves the
`z-index` property to the wrapper box.
While keyword_to_foo() does return Optional<Foo>, in practice the
invalid keywords get rejected at parse-time, so we don't have to worry
about them here. This simplifies the user code quite a bit.
This fixes the very, _very_ slow loading of https://yzy-sply.com. The
`apply_style()` method also calls into this method recursively, so we
just need to call it once instead of once per node in the continuation
chain.
To be properly compatible with calc(), the resolved() methods all need:
- A length resolution context
- To return an Optional, as the calculation might not be resolvable
A bonus of this is that we can get rid of the overloads of `resolved()`
as they now all behave the same way.
A downside is a scattering of `value_or()` wherever these are used. It
might be the case that all unresolvable calculations have been rejected
before this point, but I'm not confident, and so I'll leave it like
this for now.
Initially I added this to the existing CalculationContext, but in
reality, we have some data at parse-time and different data at
resolve-time, so it made more sense to keep those separate.
Instead of needing a variety of methods for resolving a Foo, depending
on whether we have a Layout::Node available, or a percentage basis, or
a length resolution context... put those in a
CalculationResolutionContext, and just pass that one thing to these
methods. This also removes the need for separate resolve_*_percentage()
methods, because we can just pass the percentage basis in to the regular
resolve_foo() method.
This also corrects the issue that *any* calculation may need to resolve
lengths, but we previously only passed a length resolution context to
specific types in some situations. Now, they can all have one available,
though it's up to the caller to provide it.
Our layout tree requires that all containers either have inline or
non-inline children. In order to support the layout of non-inline
elements inside inline elements, we need to do a bit of tree
restructuring. It effectively simulates temporarily closing all inline
nodes, appending the block element, and resumes appending to the last
open inline node.
The acid1.txt expectation needed to be updated to reflect the fact that
we now hoist its <p> elements out of the inline <form> they were in.
Visually, the before and after situations for acid1.html are identical.
When an element is displayed as table, an anonymous table wrapper box
needs to be created for it. Among others, the position property of the
table element is then applied to the anonymous table wrapper box
instead. If the table happens to be positioned absolutely, the table
wrapper box may become the containing block for absolutely positioned
elements inside the table.
In the original code however, anonymous layout nodes were excluded from
becoming the containing block for an absolutely positioned element.
Because of this, the containing block was calculated to be the first
suitable parent block of the table wrapper box.
This incorrect containing block would result in a crash later on when
trying to size the absolutely positioned element inside the table. To
prevent this crash, the anonymous table wrapper box is now allowed to
become the containing block for absolutely positioned elements inside
a table.
The definition of containing block for an absolutely positioned element
in the spec does not mention anything about skipping anonymous boxes.
Additionally the rules for absolute positioning of tables
(https://www.w3.org/TR/css-tables-3/#abspos-boxes-in-table-root) imply
that a table wrapper box is indeed able to be the containing block for
absolutely positioned elements.
Instead just update the existing wrapper with computed values from the
table box, to insure that upside-down "inheritance" works as expected.
This allows table fixup to run on partially updated layout trees without
adding a new layer of unnecessary wrappers every time.
Same again, although rotation is more complicated: `rotate`
is "equivalent to" multiple different transform function depending on
its arguments. So we can parse as one of those instead of the full
`rotate3d()`, but then need to handle this when serializing.
This reverts commit 76daba3069.
We're going to need separate types for the JS-exposed style values, so
it doesn't make sense for us to match their names with our internal
types.
CSS filters work similarly to canvas filters, so it makes sense to have
Gfx::Filter that can be used by both libraries in an analogous way
as Gfx::Color.
Implemented by reusing AddMask display list item that was initially
added for `background-clip` property.
Progress on flashlight effect on https://null.com/games/athena-crisis
Resulting in a massive rename across almost everywhere! Alongside the
namespace change, we now have the following names:
* JS::NonnullGCPtr -> GC::Ref
* JS::GCPtr -> GC::Ptr
* JS::HeapFunction -> GC::Function
* JS::CellImpl -> GC::Cell
* JS::Handle -> GC::Root
Now that the heap has no knowledge about a JavaScript realm and is
purely for managing the memory of the heap, it does not make sense
to name this function to say that it is a non-realm variant.
The StyleResolver can find the specified CSS values for the parent
element via the DOM. Forcing everyone to locate specified values for
their parent was completely unnecessary.
Layout nodes now only carry CSS computer values with them. The main
idea here is to give them only what they need to perform layout, and
leave the rest back in the DOM.
Another step towards not having to carry the full specified style with
us everywhere. This isn't the ideal final layout, since we're mixing
computed and used values a bit randomly here, but one step at a time.
Let's start moving away from using raw strings for CSS identifiers.
The idea here is to use IdentifierStyleValue with a CSS::ValueID inside
for all CSS identifier values.
Within the same stacking context, positioned elements must be painted
after non-positioned ones.
I added a Layout::Node::for_each_child_in_paint_order() to help with
this since it's also needed for hit testing.
This is definitely not fully-featured, but basically we now handle
the clear property by forcing the cleared box below the bottom-most
floated box on the relevant side.
I had guessed that floating boxes should somehow be hoisted up to the
nearest block ancestor that creates a block formatting context, but
that's just wrong. They move up to the nearest block ancestor like any
other box that's not absolutely (or fixed) positioned. :^)
Boxes can now be floated left or right, which makes text within the
same block formatting context flow around them.
We were creating way too many block formatting contexts. As it turns
out, we don't need one for every new block, but rather there's a set
of rules that determines whether a given block creates a new block
formatting context.
Each BFC keeps track of the floating boxes within it, and IFC's can
then query it to find the available space for line boxes.
There's a huge hack in here where we assume all lines are the exact
line-height. Making this work with vertically non-uniform lines will
require some architectural changes.
Instead of doing a CSS property lookup for the line style of each
border edge during paint, we now cache the final CSS::LineStyle to use
in the Layout::BorderData.
Inline layout nodes cannot have block children (except inline-block,
of course.)
When encountering a block box child of an inline, we now hoist the
block up to the inline's containing block, and also wrap any preceding
inline siblings in an anonymous wrapper block.
This improves the ACID2 situation quite a bit (although we still need
floats to really bring it home.)
I also took this opportunity to move all tree building logic into
Layout::TreeBuilder, to continue the theme of absolving our LayoutNode
objects of responsibilities. :^)
Bring the names of various boxes closer to spec language. This should
hopefully make things easier to understand and hack on. :^)
Some notable changes:
- LayoutNode -> Layout::Node
- LayoutBox -> Layout::Box
- LayoutBlock -> Layout::BlockBox
- LayoutReplaced -> Layout::ReplacedBox
- LayoutDocument -> Layout::InitialContainingBlockBox
- LayoutText -> Layout::TextNode
- LayoutInline -> Layout::InlineNode
Note that this is not strictly a "box tree" as we also hang inline/text
nodes in the same tree, and they don't generate boxes. (Instead, they
contribute line box fragments to their containing block!)