Commit Graph

73 Commits

Author SHA1 Message Date
Andreas Kling
9af966f87d LibWeb: Avoid unnecessary Vector copying when generating line boxes
Carry the same Vector<Gfx::DrawGlyphOrEmoji> all the way from the inline
level iterator to the final line box fragment.
2024-03-25 12:39:23 +01:00
Andreas Kling
1cea4e6407 LibWeb: Use cached UsedValues pointer in IFC and its helper classes
This avoids expensive LayoutState lookups when we already have the
pointer sitting around anyway.
2024-03-16 14:27:59 +01:00
Andreas Kling
c82d517447 LibWeb: Move line-height from NodeWithStyle to ComputedValues
There's no need for this to live in the NodeWithStyle anymore. By moving
it to ComputedValues we get all the right inheritance behavior for free.
2024-01-12 17:26:16 +01:00
Aliaksandr Kalenik
2cb0039a13 LibGfx+LibWeb: Produce font cascade list in CSS font matching algorithm
According to the CSS font matching algorithm specification, it is
supposed to be executed for each glyph instead of each text run, as is
currently done. This change partially implements this by having the
font matching algorithm produce a list of fonts against which each
glyph will be tested to find its suitable font.

Now, it becomes possible to have per-glyph fallback fonts: if the
needed glyph is not present in a font, we can check the subsequent
fonts in the list.
2023-12-10 17:32:04 +01:00
Aliaksandr Kalenik
681771d210 LibGfx+LibWeb: Calculate and save glyph positions during layout
Previously, we determined the positions of glyphs for each text run at
the time of painting, which constituted a significant portion of the
painting process according to profiles. However, since we already go
through each glyph to figure out the width of each fragment during
layout, we can simultaneously gather data about the position of each
glyph in the layout phase and utilize this information in the painting
phase.

I had to update expectations for a couple of reference tests. These
updates are due to the fact that we now measure glyph positions during
layout using a 1x font, and then linearly scale each glyph's position
to device pixels during painting. This approach should be acceptable,
considering we measure a fragment's width and height with an unscaled
font during layout.
2023-12-02 22:06:11 +01:00
Aliaksandr Kalenik
df2bc8187c LibWeb: Use actual line height to calculate float y in IFC
Before, we were using the line height from NodeWithStyle::line_height()
 to calculate the y offset for floats inside the IFC. However, this
value doesn't always correspond to the actual height of a line box. For
instance, adding a fragment for an inline-block might change the height
of the line box. With this change, we recalculate the height of the
line box after adding a new fragment and use this recalculated height
value to determine the y position for floats.

Fixes https://github.com/SerenityOS/serenity/issues/20982
2023-09-09 17:05:22 +02:00
MacDue
71baa8c31a LibWeb: Add CSSPixels::nearest_value_for(FloatingPoint)
This is intended to annotate conversions from unknown floating-point
values to CSSPixels, and make it more obvious the fp value will be
rounded to the nearest fixed-point value.
2023-08-26 23:53:45 +02:00
MacDue
360c0eb509 LibWeb: Remove implicit conversion from float and double to CSSPixels
In general it is not safe to convert any arbitrary floating-point value
to CSSPixels. CSSPixels has a resolution of 0.015625, which for small
values (e.g. scale factors between 0 and 1), can produce bad results
if converted to CSSPixels then scaled back up. In the worst case values
can underflow to zero and produce incorrect results.
2023-08-26 23:53:45 +02:00
Aliaksandr Kalenik
fce4801460 LibWeb: Replace to_px() with to_px_or_zero() in InlineFormattingContext
to_px() usage should be avoided because it might leak saturated
(infinite) values into layout calculations.
2023-08-12 20:06:01 +02:00
kamp
4ac7c41483 LibWeb: Add -libweb-left and -libweb-right text-align values
These ensure that block level elements are also left and right aligned
respectively on top of the regular text alignment, matching
-libweb-center.
2023-06-16 06:55:21 +02:00
Aliaksandr Kalenik
147c3b3d97 LibWeb+WebContent: Forbid access to underlying type of CSSPixels
Although DistinctNumeric, which is supposed to abstract the underlying
type, was used to represent CSSPixels, we have a whole bunch of places
in the layout code that assume CSSPixels::value() returns a
floating-point type. This assumption makes it difficult to replace the
underlying type in CSSPixels with a non-floating type.

To make it easier to transition CSSPixels to fixed-point math, one step
we can take is to prevent access to the underlying type using value()
and instead use explicit conversions with the to_float(), to_double(),
and to_int() methods.
2023-06-13 06:08:27 +02:00
Andreas Kling
79d2c9f3e8 LibWeb: Don't justify text lines that end in a forced break
These are treated the same as the last line in a block, per CSS-TEXT-3.
2023-06-10 21:46:33 +02:00
FalseHonesty
de9604212f LibWeb: Avoid text-aligning content that is too long for its line box
Previously, we would always respect the `text-align` property, even if
the text being aligned was too long for its line box and would be
clipped. This led to seeing the clipped middle/end of strings when we
should instead always see the beginning of the text.
2023-06-02 05:21:22 +02:00
Andreas Kling
42470d837e LibWeb: Move layout box rect helpers into FormattingContext
These are only used during layout, and always within formatting context
code, so we might as well put them in FormattingContext and avoid having
to pass the LayoutState around all the time.
2023-05-31 11:38:05 +02:00
Andreas Kling
655d9d1462 LibWeb: Make CSSPixels and Length use 64-bit (double) floating point
This fixes a plethora of rounding problems on many websites.
In the future, we may want to replace this with fixed-point arithmetic
(bug #18566) for performance (and consistency with other engines),
but in the meantime this makes the web look a bit better. :^)

There's a lot more things that could be converted to doubles, which
would reduce the amount of casting necessary in this patch.
We can do that incrementally, however.
2023-05-24 14:40:35 +02:00
Andreas Kling
e938860126 LibWeb: Make text justification work between floats
While inline content between floating elements was broken correctly,
text justification was still using the original amount of available
space (without accounting for floats) when justifying fragments.
2023-05-16 14:35:10 +02:00
Andreas Kling
bab6796099 LibWeb: Rewrite calculation of available space between floats
This code now works in terms of *intrusion* by left and right side
floats into a given box whose insides we're trying to layout.

Previously, it worked in terms of space occupied by floats in the root
box of the BFC they participated in. That created a bunch of edge cases
since the code asking about the information wasn't operating in root
coordinate space, but in the coordinate space of some arbitrarily nested
block descendant of the root.

This finally allows horizontal margins in the containing block chain to
affect floats and nested content correctly, and it also allows us to
remove a bogus workaround in InlineFormattingContext.
2023-05-16 14:35:10 +02:00
Andreas Kling
5d4e9a0673 LibWeb: Basic support for CSS text-indent: <length-percentage>
Note that this simple form of text-indent only affects the first line
of formatted content in each block.

Percentages are resolved against the width of the block.
2023-05-15 19:31:09 +02:00
Andreas Kling
e4b71495f5 LibWeb: Resolve percentage vertical-align values against line-height
...instead of not resolving them at all. :^)
2023-03-29 18:38:29 +02:00
Mathis Wiehl
9927dab993 LibWeb: Don't drop single <br/> lines
Previously, when having inline contexts consisting of just a `<br/>`
tag, we would not create a line box.

Ensure that there is always a line box when a line is explicitly being
broken and also ensure it won't be trimmed due to being empty.

This will a fix a number of sites that use `<br>` tags for layouts
between block elements (even though the spec says they shouldn't).
2023-03-16 08:40:29 +00:00
Mathis Wiehl
b96920a9d6 LibWeb: Consider margins of atomic inlines in layout
According to CSS Inline Layout Module Level 3 § 2.2 Step 1. atomic
inlines should be layed out in a line box based on their margin box.

However, up until this patch we were unconditionally considering only
the border box during line box height calculation. This made us
essentially drop all vertical margins for atomic inlines.
2023-03-14 14:45:40 +01:00
Andreas Kling
f97754942c LibWeb: Collapse margin-left with space used by left-side floats
We had an issue where boxes with margin-left were shifted right by
left-side floats twice instead of just once.
2023-03-11 10:46:26 +01:00
Sam Atkins
5d8e3f5122 LibWeb: Convert Layout::Node to new pixel units 2023-01-05 17:42:31 +01:00
Sam Atkins
76047d1932 LibWeb: Convert InlineLevelIterator/LineBox/LineBuilder to new px units 2023-01-05 17:42:31 +01:00
Sam Atkins
056acb5ebf LibWeb: Convert InlineFormattingContext to new pixel units 2023-01-05 17:42:31 +01:00
Sam Atkins
ab49dbf137 LibWeb: Convert Paintable coordinates to new pixel units
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.
2022-12-14 16:47:57 +00:00
Aliaksandr Kalenik
f0ab127a41 LibWeb: Consider strut while calculating baseline for a line
Strut should be taken in account while computing baseline of
a line. Otherwise it results in wrong alignment in boxes that
has inline elements without any text.

This also fixes red box in Acid 2.
2022-12-07 17:50:13 +01:00
Aliaksandr Kalenik
ba64d0462c LibWeb: Move box_baseline from LineBuilder.cpp to LayoutState.cpp 2022-12-05 17:47:48 +01:00
Andreas Kling
42538b5375 LibWeb: Fix bogus inline-block check in line box layout
When checking if a line box fragment "isn't just dumb inline content",
we were checking "is replaced or inline-block". What we really need to
be checking is "is replaced or inline-outside-but-not-flow-inside".
So now we check that.

This fixes an issue where inline-flex boxes were given incorrect extra
height due to being treated as regular text for purposes of line height
calculation.
2022-10-14 19:50:14 +02:00
Andreas Kling
f260afedb1 LibWeb: Don't add half-leading twice to inline block boxes
Inline-level blocks already have the half-leading applied internally,
so by adding it twice, we were offsetting their baseline by the
half-leading of the line.

This fixes an issue where inline-blocks were vertically offset from
the line they're supposed to sit on.
2022-10-03 17:22:08 +02:00
Andreas Kling
7c6e42c2d4 LibWeb: Fix bogus comparison when measuring if a float can fit
We were using `>=` instead of `>` when checking if a float with a given
width could fit in the available space. If the width was an exact match,
we rejected it! Oops :^)
2022-09-29 20:10:07 +02:00
Andreas Kling
e8a5233b94 LibWeb: Don't round fragment widths while accumulating in LineBuilder
By rounding the fragment widths, we sometimes inserted line breaks
prematurely, even though the fragment *would* fit.
2022-09-29 20:09:56 +02:00
Andreas Kling
5efd63741f LibWeb: Perform horizontal inline alignment based on available space
Previously, we were using the full containing block width as a reference
for text-align values "right" and "center". This didn't take intruding
floats into account.
2022-09-22 17:06:48 +02:00
Andreas Kling
389f47f6fe LibWeb: Check both top and bottom of float position when looking for fit
We have to check that there's enough space at both the top and bottom of
the float's margin box, otherwise we risk overlapping existing content.
2022-09-22 16:54:12 +02:00
Andreas Kling
412b2313f3 LibWeb: Improve inline flow around floating boxes
This patch combines a number of techniques to make inline content flow
more correctly around floats:

- During inline layout, BFC now lets LineBuilder decide the Y coordinate
  when inserting a new float. LineBuilder has more information about the
  currently accumulated line, and can make better breaking decisions.

- When inserting a float on one side, and the top of the newly inserted
  float is below the bottommost float on the opposite side, we now reset
  the opposite side back to the start of that edge. This improves
  breaking behavior between opposite-side floats.

- After inserting a float during inline layout, we now recalculate the
  available space on the line, but don't adjust X offsets of already
  existing fragments. This is handled by update_last_line() anyway,
  so it was pointless busywork.

- When measuring whether a line can fit at a given Y coordinate, we now
  consider both the top and bottom Y values of the line. This fixes an
  issue where the bottom part of a line would bleed over other content
  (since we had only checked that the top Y coordinate of that line
  would fit.)

There are some pretty brain-dead algorithms in here that we need to make
smarter, but I didn't want to complicate this any further so I've left
FIXMEs about them instead.
2022-09-16 15:15:50 +02:00
Andreas Kling
71ca857b67 LibWeb: Break lines until we have enough space between floats
Before this change, we'd always insert one line box fragment, even when
a float was taking up too much space on the line, and the fragment
didn't actually fit.

We now perform line breaks until we have enough space between floats.
This fixes many page layouts where we'd previously see small fragments
of inline content outside the right edge of the containing block.
2022-09-13 17:03:31 +02:00
Andreas Kling
ab6af4c9a0 LibWeb: Remove unused member LineBuilder::m_layout_mode 2022-08-14 23:33:28 +02:00
Andreas Kling
ed8930fff5 LibWeb: Add accessors for UsedValues::computed_{width,height}
This is preparation for doing some more work when assigning to these
values.
2022-07-19 15:40:41 +02:00
Andreas Kling
9b46091f38 LibWeb: Rename LayoutState::NodeState => LayoutState::UsedValues
This object contains all the CSS "used values" as seen during the layout
process, so calling it "used values" seems appropriate. :^)
2022-07-17 14:11:37 +02:00
Andreas Kling
52862c72d0 LibWeb: Rename FormattingState to LayoutState
This seems a bit more descriptive (and also a bit shorter).
2022-07-17 14:11:36 +02:00
Andreas Kling
e78282aebb LibWeb: Make sure we always apply size constraints in IFC
Pre-compute the effective containing block width in the IFC constructor
and use that throughout.
2022-07-11 18:57:45 +02:00
Andreas Kling
64959a8504 LibWeb: Express intrinsic size layout via size constraints
Previously, we had three layout modes:

- Normal:
    - Everything uses the computed values from CSS.

- MinContent:
    - Containing blocks act as if they have 0 width.
    - All line breaking opportunities are taken.

- MaxContent:
    - Containing blocks act as if they have infinite width.
    - Only forced line breaks are accepted.

The above was based on a set of misunderstandings of CSS sizing.
A major problem with the above was that *all* containing blocks
behaved differently during intrinsic size layout, not just the
relevant one.

With this patch there are only two layout modes:

- Normal:
    - Everything uses the computed values from CSS.

- IntrinsicSizeDetermination:
    - One or more boxes have size constraints applied.

There are two size constraints per layout box, set here:

- FormattingState::NodeState::width_constraint
- FormattingState::NodeState::height_constraint

They are of type SizeConstraint and can be one of None, MinContent,
or MaxContent. The default is None.

When performing an IntrinsicSizeDetermination layout, we now assign
a size constraint to the box we're trying to determine the intrinsic
size of, which is then honored by using two new helpers to query
the dimensions of containing blocks:

- FormattingContext::containing_block_width_for(Box)
- FormattingContext::containing_block_height_for(Box)

If there's a relevant constraint in effect on the Box, the size of
its containing block is adjusted accordingly.

This is essentially an implementation of the "available space"
constraints from CSS-SIZING-3. I'm sure some things will break from
this, and we'll have to deal with that separately.

Spec: https://drafts.csswg.org/css-sizing-3/#available
2022-07-11 18:57:45 +02:00
Andreas Kling
6a4247bee9 LibWeb: Use more precise font metrics when doing inline layout
We now position inline-level boxes based on ascent and descent metrics
from the font in use. This makes our basic text layouts look a lot more
like those produced by other browsers. :^)

I've tried to match the terminology used by the CSS Inline Layout spec.

This will regress Acid2 a little bit, and probably various other sites,
but on the whole it's the direction we should be heading, so let's go.
2022-03-30 01:12:44 +02:00
Andreas Kling
1c88536298 LibWeb: Use the new Gfx::Painter::draw_text_run() API for drawing text
This avoids a bunch of unnecessary work in Painter which not only took
time, but sometimes also led to alignment issues. draw_text_run() will
draw the text where we tell it, and that's it.
2022-03-30 00:57:15 +02:00
Andreas Kling
aefe1727fc LibWeb: Make text newlines in "pre" mode emit a ForcedBreak item
Instead of emitting a Text item with the "should_force_break" flag set
to true, newlines in newline-preserving text content now timply turn
into ForcedBreak items. This makes the <pre> element work again.
2022-03-26 20:04:56 +01:00
Andreas Kling
6cffabef03 LibWeb: Support CSS vertical-align values "top" and "bottom" 2022-03-24 22:57:01 +01:00
Andreas Kling
195ef5e26f LibWeb: Bring CSS line-height implementation closer to spec
We now distribute the line-height evenly between the space above and
below inline-level boxes. This noticeably improves our baseline
alignment in many cases.

Note that the "vertical-align: <length>" case is quite awkward, as the
extra height added by the offset baseline must count towards the line
box height.

There's a lot of room for improvement here, but this makes the buckets
container on Acid3 show up in the right place, with the right size.
2022-03-24 22:52:44 +01:00
Andreas Kling
ed1576eea8 LibWeb: Align baseline of inline-block with non-zero top border/padding
We were not adjusting the fragment baseline for inline-blocks that had
some border and/or padding size up top.
2022-03-24 18:30:56 +01:00
Andreas Kling
de6f7f0029 LibWeb: Support CSS floats in inline flow
CSS floats are now emitted by the InlineLevelIterator. When this
happens, IFC coordinates with the parent BFC to float the box to the
side, using the current LineBuilder state for vertical placement.

This makes the "instructions" text on Acid3 render as a single
contiguous flow of inline content.
2022-03-22 19:26:51 +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