This implements at least some of the specification. inter-character is
not yet handled. However as our current algorithm only considers
whitespace as word breaks, inter-word could technically be considered to
be handled. :^)
All the justification-related code is now in
InlineFormattingContext::apply_justification_to_fragments and is
performed after all the line boxes have been added.
Text justification now only happens on the last line if the excess space
including whitespace is below a certain threshold. 10% seemed reasonable
since it prevents the "over-justification" of text. Note that fragments
in line boxes before the last one are always justified.
Note that we don't put absolutely positioned items on a line! This is
just so that IFC can discover boxes and pass them along to BFC.
This fixes an issue where only direct children of the IFC containing
block were considered for absolute positioning. Now we pick up
absolutely positioned children of nested inline nodes as well.
Previously we used a native ui button to draw the buttons.
These buttons can however not be styled with css.
To allow these to be styled with css, we create a button with
the UA stylesheet that resembles the system ui button.
When calculating how much space is available for inline content between
left and right floated elements, we have to use coordinates in the
containing block's coordinate space, since that's what floats use.
This fixes an issue where text would sometimes overlap floats.
This patch adds a map of Layout::Node to FormattingState::NodeState.
Instead of updating layout nodes incrementally as layout progresses
through the formatting contexts, all updates are now written to the
corresponding NodeState instead.
At the end of layout, FormattingState::commit() is called, which
transfers all the values from the NodeState objects to the Node.
This will soon allow us to perform completely non-destructive layouts
which don't affect the tree.
Note that there are many imperfections here, and still many places
where we assign to the NodeState, but later read directly from the Node
instead. I'm just committing at this stage to make subsequent diffs
easier to understand.
The purpose of this new object will be to keep track of various states
during an ongoing layout.
Until now, we've been updating layout tree nodes as we go during layout,
which adds an invisible layer of implicit serialization to the whole
layout system.
My idea with FormattingState is that running layout will produce a
result entirely contained within the FormattingState object. At the end
of layout, it can then be applied to the layout tree, or simply queried
for some metrics we were trying to determine.
When doing subtree layouts to determine intrinsic sizes, we will
eventually be able to clone the current FormattingState, and run the
subtree layout in isolation, opening up opportunities for parallelism.
This first patch doesn't go very far though, it merely adds the object
as a skeleton class, and makes sure the root BFC has one. :^)
Nobody makes undefined Lengths now, (although actually removing
Undefined will come in a later commit) so we can remove this parameter,
and `resolved_or_auto()`/`resolved_or_zero()`.
Here's roughly how this works:
- InlineLevelIterator keeps a nesting stack of inline-level nodes with
box model metrics.
- When entering a node with box model metrics, we add them to the
current "leading metrics".
- When exiting a node with box model metrics, we add them to the
current "trailing metrics".
- Pending leading metrics are consumed by the first fragment added
to the line.
- Pending trailing metrics are consumed by the last fragment added
to the line.
Like before, the position of a line box fragment is the top left of its
content box. However, fragments are placed horizontally along the line
with space inserted for padding and border.
InlineNode::paint() now expands the content rect as appropriate when
painting background and borders.
Note that margins and margin collapsing is not yet implemented.
This makes the eyes on ACID2 horizontally centered. :^)
Until now, some formatting contexts (BFC in particular) have been
assigning size to the root box. This is really the responsibility of the
parent formatting context, so let's stop doing it.
To keep position:absolute working, parent formatting contexts now notify
child contexts when the child's root box has been sized. (Note that the
important thing here is for the child root to have its final used height
before it's able to place bottom-relative boxes.)
This breaks flexbox layout in some ways, but we'll have to address those
by improving the spec compliance of FFC.)
This property represents the CSS content size, so let's reduce ambiguity
by using the spec terminology.
We also bring a bunch of related functions along for the ride.
Most of the time, we cannot resolve a `calc()` expression until we go to
use it. Since any `<length-percentage>` can legally be a `calc
()`, let's store it in `LengthPercentage` rather than make every single
user care about this distinction.
While IFC flows text into a block container, floating objects are
anchored at the BFC root, not necessarily the local block container.
Because of this, we have to use root-relative coordinates when checking
how much space is available in between left and right floated objects.
This patch adds a BFC::FloatSideData struct so we can contain left and
right floating object layout state in a struct. This is preparation for
adding more per-side state.
When collapsing whitespace, we can skip over all-whitespace chunks at
the start of each line, and immediately following fragments that
themselves end in whitespace.
This resolves a long-standing architectural problem in LibWeb that made
it unable to place CSS floating objects correctly due to not having
final vertical position information when computing the amount of
available horizontal space for each line.
This patch adds a new mechanism that allows InlineFormattingContext to
build line boxes incrementally instead of all-in-one go.
Incremental build will eventually allow much better support for CSS
floating objects.
Despite looking like it was still needed, it was only used for passing
to other calls to Length::resolved() recursively. This makes the
various `foo.resolved().resolved()` calls a lot less awkward.
(Though, still quite awkward.)
I think we'd need to separate calculated lengths out to properly tidy
these calls up, but one yak at a time. :^)
A lot of this is quite ugly, but it should only be so until I remove
Length::Type::Percentage entirely. (Which should happen later in this
PR, otherwise, yell at me!) For now, a lot of things have to be
resolved twice, first from a LengthPercentage to a Length, and then
from a Length to a pixel one.
I don't remember why we did things this way, but it's clearly not right
to stretch fragments vertically. Instead, we should just align their
bottom to the appropriate line (as we already do.)
Per the spec, only a BlockContainer" can have line boxes, so let's not
clutter up every Layout::Box with line boxes.
This also allows us to establish an invariant that BFC and IFC always
operate on a Layout::BlockContainer.
Note that if BlockContainer has all block-level children, its line boxes
are not used for anything. They are only used in the all inline-level
children scenario.
There's a subtle difference here. A "block box" in the spec is a
block-level box, while a "block container" is a box whose children are
either all inline-level boxes in an IFC, or all block-level boxes
participating in a BFC.
Notably, an "inline-block" box is a "block container" but not a "block
box" since it is itself inline-level.
This commit unifies methods and method/param names between the above
classes, as well as adds [[nodiscard]] and ALWAYS_INLINE where
appropriate. It also renamed the various move_by methods to
translate_by, as that more closely matches the transformation
terminology.
SPDX License Identifiers are a more compact / standardized
way of representing file license information.
See: https://spdx.dev/resources/use/#identifiers
This was done with the `ambr` search and replace tool.
ambr --no-parent-ignore --key-from-file --rep-from-file key.txt rep.txt *
(...and ASSERT_NOT_REACHED => VERIFY_NOT_REACHED)
Since all of these checks are done in release builds as well,
let's rename them to VERIFY to prevent confusion, as everyone is
used to assertions being compiled out in release.
We can introduce a new ASSERT macro that is specifically for debug
checks, but I'm doing this wholesale conversion first since we've
accumulated thousands of these already, and it's not immediately
obvious which ones are suitable for ASSERT.