Commit Graph

68 Commits

Author SHA1 Message Date
Andreas Kling
27c68624b6 LibWeb: Resolve top/bottom inset properties for position:relative
This patch reimplements inset property resolution based on the new
CSS Positioned Layout specification. Nothing should change for
left/right insets, but we gain support for top/bottom. :^)
2022-03-27 18:16:09 +02:00
Andreas Kling
fa71401bec LibWeb: Rename ComputedValues::offset() => inset() 2022-03-27 18:16:08 +02:00
Andreas Kling
d77dfc6b48 LibWeb: Rename FormattingContext::compute_position() => compute_inset()
This function computes the used inset properties, not the position of a
box per se, so let's call it something more accurate.
2022-03-27 18:16:08 +02:00
Andreas Kling
83a2aa1832 LibWeb: Include negative margins in height:auto computation for BFC root
...but never allow the resulting height to become negative. This solves
an issue seen on Acid3 where elements with negative vertical margins
expanded the size of their height:auto container instead of shrinking
it, which is the correct behavior. This now works :^)
2022-03-26 22:51:10 +01:00
Andreas Kling
fe908e7db2 LibWeb: Rename "offset" in box model metrics to "inset"
The CSS Positioned Layout spec refers to the top/left/bottom/right
properties as "inset" properties, so let's use the same terminology.
2022-03-26 17:31:01 +01:00
Andreas Kling
925c34cf43 LibWeb: Include floats in height:auto for BFC root with inline children
BFC roots with children_are_inline()==true can still have floating boxes
as well. children_are_inline() is only concerned with in-flow children.

For this reason, we have to always consider floats when calculating
height:auto for BFC roots.
2022-03-26 00:15:25 +01:00
Andreas Kling
c02e6f991a LibWeb: Improve vertical margin collapse between adjacent blocks
Collect all the preceding block-level siblings whose vertical margins
are collapsible. Both margin-top and margin-bottom now (previously,
we only considered the margin-bottom of siblings.)

Use the right margin in part-negative and all-negative situations.
2022-03-25 00:10:09 +01:00
Andreas Kling
c1f0d21bbe LibWeb: Rename the LayoutMode enum values and explain them
The old mode names, while mechanically accurate, didn't really reflect
their relationship to the CSS specifications.

This patch renames them as follows:

    Default => Normal
    AllPossibleLineBreaks => MinContent
    OnlyRequiredLineBreaks => MaxContent

There's also now an explainer comment with the LayoutMode enum about the
specific implications of layout in each mode.
2022-03-19 15:46:15 +01:00
Andreas Kling
39b7fbfeb9 LibWeb: Rewrite CSS float implementation to use offset-from-edge
The previous implementation used relative X offsets for both left and
right-side floats. This made right-side floats super awkward, since we
could only determine their X position once the width of the BFC root was
known, and for BFC roots with automatic width, this was not even working
at all most of the time.

This patch changes the way we deal with floats so that BFC keeps track
of the offset-from-edge for each float. The offset is the distance from
the BFC root edge (left or right, depending on float direction) to the
"innermost" margin edge of the floating box.

Floating box are now laid out in two passes: while going through the
normal flow layout, we put floats in their *static* position (i.e the
position they would have occupied if they weren't floating) and then
update the Y position value to the final one.

The second pass occurs later on, when the BFC root has had its width
assigned by the parent context. Once we know the root width, we can
set the X position value of floating boxes. (Because the X position of
right-side floats is relative to the right edge of the BFC root.)
2022-03-18 15:18:48 +01:00
Andreas Kling
915ee66bd6 LibWeb: Implement shrink-to-fit layout on top of intrinsic size cache
Using the intrinsic size cache means we only perform the nested layout
to determine intrinsic size *once* per root layout pass.

Furthermore, by using a throwaway FormattingState, details of the nested
layout can't leak into and mutate the outer layout.
2022-03-18 15:18:48 +01:00
Andreas Kling
39ca39204b LibWeb: Cache intrinsic sizes on the root FormattingState
Instead of caching them with the current state, we can cache them at the
root of the state tree. Since intrinsic sizes are immutable during the
same layout, this allows layout to take advantage of intrinsic sizes
discovered during nested layout (and avoids a *lot* of duplicate work.)
2022-03-18 15:18:48 +01:00
Lenny Maiorani
c37820b898 Libraries: Use default constructors/destructors in LibWeb
https://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines#cother-other-default-operation-rules

"The compiler is more likely to get the default semantics right and
you cannot implement these functions better than the compiler."
2022-03-17 17:23:49 +00:00
Andreas Kling
2f3d7a7c36 LibWeb: Swap min-content and max-content intrinsic sizes if needed
I'm a little confused about intrinsic heights *really* work, and I'm
struggling to extract that information from the spec. In the meantime,
let's ensure that min-content is always smaller than (or equal to)
max-content so that other math works as expected.
2022-03-13 00:04:51 +01:00
Andreas Kling
515db5fc1b LibWeb: Make Layout::FormattingState copies shallow
Previously, each NodeState in a FormattingState was shared with the
parent FormattingState, but the HashMap of NodeState had to be copied
when making FormattingState copies.

This patch makes copying instant by keeping a pointer to the parent
FormattingState instead. When fetching immutable state via get(), we may
now return a reference to a NodeState owned by a parent FormattingState.

get_mutable() will copy any NodeState found in the ancestor chain before
making a brand new one.
2022-03-13 00:04:51 +01:00
Andreas Kling
3f2b17f602 LibWeb: Add functions for calculating intrinsic sizes of a Layout::Box
FormattingContext can now calculate the intrinsic sizes (min-content and
max-content in both axes) for a given Layout::Box.

This is a rather expensive operation, as it necessitates performing two
throwaway layouts of the subtree rooted at the box. Fortunately, we can
cache the results of these calculations, as intrinsic sizes don't change
based on other context around the box. They are intrinsic after all. :^)
2022-03-13 00:04:51 +01:00
Andreas Kling
bc82b3eaec LibWeb: Improve height:auto for position:absolute blocks
Previously we were not giving automatic height to position:absolute
blocks that had both top:auto and bottom:auto.
2022-03-05 21:26:19 +01:00
Andreas Kling
1d6cf3a43f LibWeb: Consider all children when doing height:auto for normal blocks
Only measuring from the top to the last in-flow block was not giving us
enough height in many cases.
2022-03-03 12:57:53 +01:00
Andreas Kling
fa43a4118e LibWeb: Handle height:auto separately for BFC root vs other block boxes
I was wrong in 56df05ae44, there are
situations where floating children should not affect the auto height of
their parent.

It turns out we were using the "height:auto for BFC roots" algorithm for
all height:auto blocks. This patch fixes that by splitting it into two
separate functions, and implementing most of the two different variants.

Note that we don't support vertical margin collapsing here yet.

Thanks to Tim for noticing the error! :^)
2022-03-01 23:26:35 +01:00
Andreas Kling
56df05ae44 LibWeb: Always include floats when computing height:auto for blocks
I'm not sure why we had two modes for this, but floats should always be
included in the auto height AFAICT.
2022-03-01 19:01:19 +01:00
Andreas Kling
6478b460fb LibWeb: Fix wrong height:auto computation for block with floating child
If an element with height:auto has any floating descendants whose bottom
margin edge is below the element's bottom content edge, then the height
is increased to include those edges.

Before this patch, we were stopping at the bottom *content* edge of
floating descendants.
2022-03-01 19:01:19 +01:00
Andreas Kling
c6cf240f9a LibWeb: Store bottom edge location with each LineBox
Previously we were computing the bottom edge of a line box by finding
the bottommost fragment on the line.

That method didn't give correct results for line boxes with no fragments
(which is exactly what you get when inserting a bunch of <br> elements.)

To cover all situations, we now keep track of the bottommost edge in the
LineBox object itself.
2022-02-28 14:17:44 +01:00
Andreas Kling
4b6295e667 LibWeb: For height:auto blocks, measure from top of *top* line box
We were incorrectly checking for negative top edges in the *last* line
box only.
2022-02-28 14:17:44 +01:00
Andreas Kling
726edd2d3b LibWeb: Pass state to create_independent_formatting_context_if_needed()
Instead of using the current m_state implicitly, make this function take
a FormattingState&. This will allow us to use it for throwaway layouts.
2022-02-28 14:17:44 +01:00
Andreas Kling
797f51e122 LibWeb: Add border box top/bottom metrics to line box fragments
This will allow us to support more kinds of vertical alignment.
2022-02-26 09:24:40 +01:00
Andreas Kling
db5bf6e64c LibWeb: Rename FormattingState::ensure() -> get_mutable()
This makes it much more obvious what the difference between get() and
get_mutable() is.
2022-02-21 18:35:12 +01:00
Andreas Kling
0f15d1f947 LibWeb: Add hack to avoid crashing on !child_display.is_flow_inside()
When encountering a box that claims to have block-level children, but
its CSS display type isn't actually "flow" inside, we would previously
crash due to a VERIFY() failure.

However, many sites choke on this due to freestanding table-related
boxes like those created by "table-row" and "table-row-group".
We're supposed to fix those up by wrapping them in a full set of table
boxes during layout tree construction, but that algorithm obviously
isn't working correctly in all cases. So let's work around the crashes
for now, allowing many more sites to load (even if visually incorrect.)

This is a rather monstrous hack, and we should get rid of it as soon as
it's not needed anymore.
2022-02-21 18:35:12 +01:00
Andreas Kling
c9700e100e LibWeb: Start making our layout system "transactional"
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.
2022-02-21 18:35:12 +01:00
Andreas Kling
561612f219 LibWeb: Add Layout::FormattingState
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. :^)
2022-02-21 18:35:12 +01:00
Sam Atkins
f0a4b33a5e LibWeb: Resolve Lengths to pixels earlier 2022-02-18 19:04:37 +01:00
Sam Atkins
b715943035 LibWeb: Remove redundant Length::resolved() calls
Now that calc() is also resolved in to_px(), code in the form
`foo.resolved(bar).to_px(bar)` can be simplified to `foo.to_px(bar)`.
2022-02-18 19:04:37 +01:00
Sam Atkins
67066c5140 LibWeb: Remove fallback value from Length::resolved()
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()`.
2022-02-18 19:04:37 +01:00
Sam Atkins
5b2482a939 LibWeb: Use Optional instead of undefined-lengths for widths/heights 2022-02-18 19:04:37 +01:00
Andreas Kling
0532d7d255 LibWeb: Stop sizing the context root box in formatting contexts
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.)
2022-02-12 22:30:50 +01:00
Andreas Kling
0608de8c12 LibWeb: Rename Layout::Box::size() to content_size()
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.
2022-02-06 01:07:47 +01:00
Sam Atkins
ce0de4b2b4 LibWeb: Allow LengthPercentage to hold a calculated value
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.
2022-02-04 13:52:02 +01:00
Andreas Kling
00bd17034d LibWeb: Make IFC aware that its parent is always a BFC
This simplifies some code and allows us to use tighter types for the
parent context everywhere.
2022-01-24 02:09:17 +01:00
Sam Atkins
bfcbab0dcf LibWeb: Remove reference_for_percent parameter from Length::resolved()
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. :^)
2022-01-20 00:04:10 +01:00
Sam Atkins
dc681913e8 LibWeb: Convert width/height and min-/max- versions to LengthPercentage
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.
2022-01-20 00:04:10 +01:00
Sam Atkins
7196570f9b LibWeb: Cast unused smart-pointer return values to void 2021-12-05 15:31:03 +01:00
Andreas Kling
37f0bd0a42 LibWeb: Small fix to height computation for block-with-inline-children
We now compute the used height of height:auto by measuring from the top
content edge (y=0) to the bottom of the bottommost line box within the
block container.

This fixes an issue where we'd fail to account for the topmost line box
being taller than any of its fragments (which can happen if the
line-height is greater than the height of all fragments on the line.)
2021-10-28 19:20:40 +02:00
Ben Wiederhake
934360583f LibWeb: Remove duplicated auto height computation
Note that these two implementation differ, but the one in
FormattingContext.cpp seems to be more complete. It is also more recent.
2021-10-28 13:33:33 +02:00
Ben Wiederhake
c55527944c LibWeb: Convert const pointer to nonnull into a reference 2021-10-21 01:08:14 +02:00
Andreas Kling
e6e00d2a4d LibWeb: Remove already-fixed FIXME in creates_block_formatting_context()
We're already creating a BFC for children of inline-flex boxes.
2021-10-19 19:14:11 +02:00
Andreas Kling
dabbade05c LibWeb: Factor out creation of independent formatting contexts
This patch breaks FormattingContext::layout_inside() into two functions,
one that creates an independent formatting context (if needed), and
another that calls the former and then performs the inside layout within
the appropriate context.

The main goal here was to make layout_inside() return the independent
formatting context if one was created. This will allow us to defer
certain operations in child contexts until the parent context has
finished formatting the child root box.
2021-10-17 22:18:59 +02:00
Andreas Kling
f2d0e8d0ee LibWeb: Expose FormattingContext type
Instead of having a virtual is_block_formatting_context(), let's have a
type() that can tell you exactly which type of formatting context it is.
2021-10-17 22:18:59 +02:00
Andreas Kling
7b0cd15495 LibWeb: Cleanup + spec comments in replaced element height computation 2021-10-14 23:22:59 +02:00
Andreas Kling
463ee07c00 LibWeb: Cleanup + spec comments in replaced element width computation 2021-10-14 23:22:59 +02:00
Andreas Kling
a58cc2eeb4 LibWeb: Rename "intrinsic ratio" => "intrinsic aspect ratio" 2021-10-14 18:48:49 +02:00
Andreas Kling
81590b1804 LibWeb: Make intrinsic width/height/ratio a Box concept and simplify it
Apparently it's not only replaced elements that can have intrinsic
sizes, so let's move this concept from ReplacedBox to Box. To avoid
bloating Box, we make the accessors virtual.
2021-10-14 18:39:27 +02:00
Andreas Kling
f73aa8e2bd LibWeb: Move line boxes from Layout::Box to BlockContainer
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.
2021-10-06 21:53:25 +02:00