When calculating the edge offset of the next floating item based on the
offset of the preceding floating item, we need to ensure that the
preceding offset is always > 0. This isn't explicitly written in the
spec, but all other popular engines do that.
Fixes https://github.com/SerenityOS/serenity/issues/21023
Before, we completely ignored clearance for block-level boxes if they
were floated. This was incorrect because it is valid for a block-level
box to be floated and still have clearance. However, unlike clearance
on normal flow boxes, clearance on floating boxes does not affect the
y-position of subsequent normal flow boxes. Instead, it pushes the
box's position to the very beginning of an edge.
Work towards https://github.com/SerenityOS/serenity/issues/21023
This is a simple mapping of some code-points to others.
I was going to use a ref test for this, but without knowing that the
font contains the code-points (which SerenitySans does not) we'd be
comparing a series of replacement characters to a series of replacement
characters, which doesn't tell you if they're the right code-points
underneath.
This one is a bit fun because it can be `add(<integer>)` or `auto-add`,
but children have to inherit the computed value not the specified one.
We also have to compute it before computing the font-size, because of
`font-size: math` which will be implemented later.
Previously we were overly generous in what we accepted for `display`.
For example, we accepted:
- Multiple outside or inside identifiers
- Unrecognized identifiers
- Non-identifier tokens
Now, we reject all these cases. This makes us stop accepting the
`display: block math;` declaration in `MathML/Default.css`, and so the
one layout test we have containing MathML elements has changed to
reflect that.
When a button should use flex for alignment and also has ::before
and/or ::after, we previously did the following:
1. Prepended/appended the button's children with boxes for
pseudo-elements.
2. Replaced the button's direct children with a flex container that
contains its children.
As a result, the generated boxes for ::before/::after ended up as
children of the generated flex item, instead of being direct children
of the button layout box as they were supposed to be.
This change reverses these steps, ensuring that boxes for
pseudo-elements are generated only after modifications inside the
button layout are completed.
When modifying the button layout during tree building to use flex for
vertical alignment, let's explicitly set the button box's children to
be non-inline. It doesn't make sense to layout the button as an IFC
when its only child is a flex container.
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
If an element is created from JS, it might have its contents modified
before it is inserted into the document. In this case, we don't have a
shadow tree yet and so trying to set m_text_node's text content would
cause a null dereference. So let's not do that. :^)
That case also means that by the time we do create the shadow tree, we
have the text content already, so we can set it there.
Added a test to verify that we don't crash, and that the text content
appears in the textarea whether it was inserted by JS or by the HTML
parser.
This change allows IDL interfaces to be compiled using new AK String
which have a attribute in the interface that may return null.
Without this change we would run into a compile error from code such as
the following example:
```
auto retval = impl->deprecated_attribute(HTML::AttributeNames::ref);
if (!retval.has_value()) {
return JS::js_null();
}
return JS::PrimitiveString::create(vm, retval.release_value());
```
As `deprecated_attribute` returns a `DeprecatedString` instead of an
`Optional<String>`. Fix that by using the non-deprecated attribute
implementation, and falling back to the empty string for where we cannot
return null.
Also add a test here to cover a regression I almost introduced here
which was not previously covered by our test suite.
Ideally, all of this should actually just be calling
Element::get_attribute_value, but I'm not entirely sure at this stage
what the behavioral change would be to test for here. Since this
implementation preserves the previous behavior, stick with it, and add a
FIXME for now.
Previously, the code assumed that in dividing up the space in the
affected tracks there would never be an overshoot. Instead, we can
check for each track how much extra space is left and never consume any
extra.
In the same way, we can ensure that all extra space is consumed by
distributing all remaining extra space starting from the first track.
Thus, if there is no growth limit, the space distribution should always
consume all the extra space.
The spec says that the sum of affected size + item-incurred increase
should reach the limit, rather than just the item-incurred increase.
This seems to improve layout on the testcase `row-span-2-with-gaps`.
The extra line of space at the bottom of the left div
(`div.grid-item.item-span-two`) is not present anymore, matching other
browsers' layout much more closely.
Support the optional `<attr-type>` parameter to the `attr()` function,
which allows parsing the attribute's value as a variety of types,
instead of always as a string.
The final used values for these properties is stored in the layout node,
so we need to make sure they are propagated there as well when doing
table box fixup.
Some replaced elements can have intrinsic aspect ratios but no
intrinsic size. In these cases, the tentative sizes are undefined, and
can therefore sometimes be zero. However, when resolving the size
constraints, we are already guaranteed to have an intrinsic aspect
ratio, so let's use that instead to calculate the resolved sizes.
This allows us to retain perfect precision for aspect ratios derived
from either the intrinsic sizes of replaced elements, or the
`aspect-ratio` CSS property.
There's a particularly awkward case where the static position of an
abspos child of a flex container is dependent on its height. This can
happen when `align-items: center` is in effect, as we have to adjust
the abspos child's Y position by half of its height.
This patch solves the issue by reordering operations in the abspos
height resolution algorithm, to make sure that height is resolved
before the static position is calculated.
Using flex layout inside button solves the issue with wrongly calculated
height when it has: pseudo element and whitespaces inside.
Also using flex instead of a table layout allows for the same vertical
alignment but with fewer layout nodes: a flex container and anonymous
wrapper for content instead of a table wrapper, table, row, and cell.
Before this change, we were creating a new anonymous flex item for every
inline-level child of a flex container, even when we had a sequence of
inline-level children.
The fix here is to simply keep putting things in the last child of the
flex container, if that child is already an anonymous flex item.
Previously we always set the height of the HTML element equal to the
viewport height but now this will only happen in quirks mode as it is
intended. Otherwise the html element height will be computed as auto.
Rather than modify the transform of the parent (which could change
independently), this adds a new override element_transform() where
element specific tranfroms can be applied. This will always stay in
sync with the attributes.
A ref test comparing a .svg and .html version of the same file is
added as due to differences in attribute parsing order, the .svg version
was previously drawn incorrectly.
Fixes#20859
None of the existing tests contain a URL which has a fragment in them,
but this does verify that the URL parser does not actually find any!
Also, this should let us verify the correctness of URLs which actually
do contain fragments.
Since we always pass the px value as an argument to resolved(), we can
pass it directly as CSSPixels instead of wrapping it in Length. This
approach allows us to avoid converting to a double, resulting in fewer
precision issues.
When text paintables shift around in the tree due to line wrapping,
we may end up in a situation where some text node does not generate
a paintable (due to being all whitespace, for example), even though
in the previous layout pass, it *did* generate a paintable.
To prevent holding on to old paintables in such cases, we now do a
pass in LayoutState::commit() where we explicitly detach all old
paintables from the layout tree.
We need to start looking from the beginning of current row if adding
new implicit column track made enough space to accomodate spanning item
This fixes placement for spanning grid items when `grid-auto-flow` is
specified to `column`.
If the table used width has to be adjusted because of a cell with
percentage width, add back the undistributable space due to border
spacing. This is consistent with the width distribution algorithm, which
sets aside the undistributable space and the behavior of other browsers.
When the grid layout gets to
`resolve_items_box_metrics(GridDimension::Column)`, we've already
determined the width of each column. However, the widths of the
individual grid items themselves haven't been set. Rather than using
`get_available_space_for_item()`, which returns an indefinite size if
an item's width/height hasn't been set, we should use the already
known track width as the available size to calculate the fit-content
width.
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.
This means StyleComputer::resolve_unresolved_style_value() always
returns a value, so we can change its return type.
However, it does still return an UnresolvedStyleValue sometimes, so we
can't remove those checks from the user code.
- Ambiguous `raw_value()` method is replaced with `line_number()` and
`span()`.
- `line_name()` that before returned either line name or area name is
replaced with `line_name()` and `area_name()`.
- `Position` type is replaced with `Line` and `Area` type so we don't
have to guess while doing layout.
Affected test expectations:
- `template-lines-and-areas` - improvement over what we had before.
- `named-tracks` - rebaseline a giant test. will have to split it into
smaller tests in the future.
The invariants for these property getters are supposed to be checked by
the has_entries_and_events_disabled AO, but we don't have all the
plumbing hooked up between Navigables and Navigation yet.
Add a test to make sure that these methods don't assert when calling
them on a fresh page.
The resolution is simply 1dppx * device pixel ratio.
This makes high resolution images show up on https://apple.com/
when running on a high-DPI display. :^)
Note that we don't load the local font as specified, but at least we no
longer reject such src properties in the CSS parser.
This makes the custom fonts used on http://apple.com/ actually load. :^)
We were previously setting the end position of attribute names in self-
closing HTML tags to the end of the attribute value. To illustrate the
previous behavior, consider this tag and its attribute's start and end
positions (shown inclusively below):
<meta charset="UTF-8" />
^ name start
^ value start
^ value end
^ name end
Rather than setting the end position of the attribute name when we parse
the closing slash, ensure the end position is already set while we are
in the AttributeName state. We now have:
<meta charset="UTF-8" />
^ name start
^ name end
^ value start
^ value end
The tokenizer unit test has been extended to test these positions.
To illustrate the previous behavior, consider these tags and their start
and end positions (shown inclusively below):
Start tag: End tag:
<span> </span>
^ start ^ start
^end ^end
The start position of a tag is the first ASCII-alpha code point after
the opening brace. The start position of a close tag is the slash just
before the first ASCII-alpha code point. And the end position of both
is the closing brace. So the opening brace is not included in the
emitted tag, but the closing brace is. And the end tag including the
slash is an oddity that had to be worked around in its only use case
(syntax highlighting).
We now consistently exclude the braces from the emitted tag, and also
exclude the slash from the end tag, so that it does not need to be
accounted for in syntax highlighting. That is, we now have:
Start tag: End tag:
<span> </span>
^ start ^ start
^end ^end
The tokenizer unit test has been extended to test these positions.
`DeprecatedString::to_int` calls `StringUtils::convert_to_int`
internally. However, the integer parsing is not done in an HTML
spec-compliant way. For example, `colspan="2;"` is valid according to
the spec. But, with the current implementation, we will fail to parse
"2;", and instead fall back to using 1 as the colspan value.
This patch changes the `HTMLTableCellElement::col_span` and
`HTMLTableCellElement::row_span` methods to use the
`Web::HTML::parse_non_negative_integer` function that will parse the
attribute value in an HTML spec-compliant way.
We have code inside LibWeb that uses the
`AK::StringUtils::convert_to_uint`and `AK::StringUtils::convert_to_int`
methods for parsing integers. This works well for the most part, but
according to the spec, trailing characters are allowed and should be
ignored, but this is not how the `StringUtil` methods are implemented.
This patch adds two new methods named `parse_integer` and
`parse_non_negative_integer` inside the `Web::HTML` namespace that uses
`StringUtils` under the hood but adds a bit more logic to make it spec
compliant.
This giant grid test has been a source of problems while iterating on
GFC for a long time. Let's split it into smaller tests to make it
easier to identify issues without needing further reductions on the
test.
These two ref-tests involve two boxes positioned in the same place, with
outlines. Outlines always have a border-radius, meaning that the corner
pixels are not 100% opaque. (It seems to be 254 instead of 255.) With
the test files painting two outlines, and the ref test only painting
one, slight changes in the background color of the page would make that
slight variation visible sometimes. So, let's avoid that inconsistency
by always painting one outline instead of two.
We were relying on the table fixup algorithm to insert the missing table
row, which fails to do so when we only have an image in the button.
While that might be a problem with the table fixup algorithm, we should
build a correct layout tree explicitly anyway.
Fixes crashes on GitHub.
Fixes bug when "clip" property does not affect abspos children.
This change makes "clip" property to be applied together with
"overflow: hidden" in `apply_clip_overflow_rect()` that already
handles abspos children correctly.
This matches if the element has a placeholder, and that placeholder is
currently visible. This applies to `<input>` and `<textarea>` elements,
but our `<textarea>` is very limited so does not support placeholders.
Grid specification https://www.w3.org/TR/css-grid-2/#z-order defines
special painting order for grid items which should be the same as for
defined for inline-blocks in CSS2.
Check the width of the next token after white space to decide line
breaks. The next width can also be the total width of multiple tokens.
This better follows the CSS Text specification and matches behavior of
other browsers.
Fixes#20388.
We still don't know how to resolve font-relative lengths in <img sizes>
since we don't always have font size information available at this stage
in the pipeline, but we can at least handle viewport-relative lengths.
This fixes an issue on many websites where low-resolution images were
loaded (appropriate for a small viewport) even when the viewport is big.
And treat them as "auto" for now, per CSS-SIZING-3, with a FIXME about
supporting more layout directions.
This fixes an issue on MDN where `height: max-content` was not
overriding height from non-CSS presentational hints.
CSS-ALIGN-3 tells us that `normal` behavior inside flex containers is
simply to behave as `stretch` so this patch makes them behave the same
inside FFC.
Furthermore, we change the `align-items` initial value to `normal`,
matching other engines.
I'm about to make StackingContext traverse the paintable tree instead of
actually traversing the layout tree, and it turns out we were not
creating paintables for these SVG elements.
Also switch them to Layout::Box instead of the default InlineNode to
make the trees look a bit less weird. Ultimately, we should do something
specialized for these subtrees, but for now this'll do.
This patch just adds the new root paintable and updates the tests
expectations. The next patch will move painting logic from the layout
viewport to the paint viewport.
Instead of assuming that the WebDriver binary is in PATH or the two
most common build directories for our scripts, add a command line
argument to the script to pass a WebDriver binary.
We should be documenting required pacakges elsewhere and installing them
in the setup step of CI.
This also fixes a problem where the run script would fail if you already
had a cloned wpt directory.
This has to cheat and use a screenshot but thanks to the "Take Full
Screenshot" feature of Ladybird, it is very easy to update this test.
The steps are documented in the test.
When a box is sized under max-content constraint, any percentage value
set for max-width should be considered as if it were infinite. In other
words, it should have no effect on restricting the box's width.
This doesn't seem to actually have fixed any bugs, as having
FillOpacity instead of StrokeOpacity in the call to parse_css_value
doesn't seem to have actually been causing bugs. But, I still think it's
worthwhile correcting.
The reason that it wasn't causing bugs is that having FillOpacity
instead of StrokeOpacity in the call to parse_css_value means that when
parsing the value is compared to the acceptable values for that property
(for example the value can only be a percentage, or a number, etc.). In
this case both FillOpacity and StrokeOpacity seem to accept the same
values.
The SVG G container should have the same size as its children. This
fixes a bug when there was an opacity value on the G element, as in
StackingContext it would try and get a bitmap of the element which would
be empty due to it having no size.
We now keep the color value as a StyleValue up until we go to paint the
gradient, which makes `currentColor` work, along with any other color
values that can't be immediately converted into a `Gfx::Color` while
parsing.
If we run an inline script from the HTML parser, it may append a text
node to the current insertion point.
If there was text content immediately following the script element,
we would previously overwrite the script-inserted text content, due to
an oversight in the way we select an appropriate insertion point
This patch fixes the issue by only inserting parser content into
existing text nodes if they are empty.
Instead of applying relative offsets (like position:relative insets)
during painting and hit testing, we now do a pass at the end of layout
and assign the final resolved offsets to paintables.
This makes painting and hit testing easier since they don't have to
think about relative offsets, and it also fixes a bug where offsets were
not applied to text fragments inside inline-flow elements that were
themselves position:relative.
The ref tests runner takes screenshots of both the input page and the
expected page, then compares them. Ref testing allows us to catch
painting bugs, which cannot be detected with the layout and text tests
we already have.
With ref tests, we'll likely want to reuse the same expectation page
for multiple inputs. Therefore, there's a `manifest.json` file that
describes the relationship between inputs and expected outputs.
Changing `calculate_min_content_heigh()` and
`calculate_min_content_heigh()` to accept width as `CSSPixels`, instead
of `AvailableSize` that might be indefinite, makes it more explicit
that width is supposed to be known by the time height is measured.
This change has a bit of collateral damage which is rows height
calculation regression in `table/inline-table-width` that worked before
by accident.
We now apply MathML's default user agent style sheet along with other
default styles. This sheet is not mixed in with the other styles in
CSS/Default.css because it is a namespaced stylesheet and so has to
be its own sheet.
Using avilable space directly while resolving table container width
allows to avoid assigning it to table wrapper box content width which
sometimes involves infinite (saturated) values.
Also this allows to get rid of set_max_content_width() which is a hack
that allows to bypass set_content_width() to assign infinite
(saturated) width to a box.
Closes https://github.com/SerenityOS/serenity/issues/19521
Replicate the more conservative way it's done for other nodes, for
which we verify whether they have a paintable before doing
painting-related operations with it.
Fixes crash on https://www.haiku-os.org/.
Returning greatest_child_width() from automatic_content_width() in BFC
if root box children are inline and there are min/max-width that caused
width to be changed after IFC layout while content_width should be
always set to correct value by layout_inline_children() regardless of
layout mode.
- Out-of-flow items should not affect grid layout
- "The static position of an absolutely-positioned child of a grid
container is determined as if it were the sole grid item in a grid
area whose edges coincide with the content edges of the grid
container."
This object is available as `window.internals` (or just `internals`) and
is only accessible while running in "test mode".
This first version only has one API: gc(), which triggers a garbage
collection immediately.
In the future, we can add more APIs here to help us test parts of the
engine that are hard or impossible to reach via public web APIs.
This could happen if a sequence of '0' parts was followed by a longer
sequence of '0' parts at the end of the host. The first sequence was
being used for the compress, and not the second.
For example, [1:1:0:0:1:0:0:0] was being serialized as: [1:1::1:0:0:0]
instead of [1:1:0:0:1::].
Fix this by checking at the end of the loop if we are in the middle of a
sequence of '0' parts that is longer than the current longest.
We were already parsing non-function-syntax :host, so let's also do
the :host(...) variant. Note that we don't have matching for these yet.
This fixes many issues on sites generated by Wix, as they often have
selector lists that include some :host() selector, and we'd reject the
entire rule after failing to parse it.
This stuff is pretty hairy since the specifications don't give any
guidance on which widths to use when calculating the intrinsic height of
flex items in a column layout.
However, our old behavior of "treat anything indefinite as fit-content"
was definitely not good enough, so this patch improves the situation by
considering values like `min-content`, `max-content` and `fit-content`
separately from `auto`, and making the whole flex layout pipeline aware
of them (in the cross axis context).
Use the max-width of percentage cells instead of min-width as the
reference to be used to compute the total table width. The specification
only suggests that the UA should try to satisfy percentage constraints
and this behavior is more consistent with other browsers.
Fixes infinite spinning in the cases when CSSPixels does not have
enough precision to represent increase per track which happens when
very small extra_space got divided by affected tracks number.
Change associativity in computing of replaced element size to improve
precision of division.
Fixes vertically squashed image from Mozilla splash page MDN example.
This patch implements "Overflow Viewport Propagation" from CSS-OVERFLOW.
It fixes an issue where many websites were not scrollable because they
had `overflow: scroll` on the body element and we didn't propagate it.
Pseudo-elements like ::before and ::after were discarded when their
content property was an empty string (ignoring whitespace), because they
are anonymous containers with no lines.
Our previous way around it was to add an empty line box (see b062a0fb7c)
however it didn't actually work for cases described in the previous
commit.
This makes avatars and cover arts square on last.fm and "fixes" the test
css-pseudo-element-should-not-be-affected-by-presentational-hints.html.
Unfortunately, this also regresses on block-and-inline/clearfix.html,
but that hopefully will be handled in subsequent commit.
Properties like min-width, max-width, etc, should be ignored while we're
trying to determine the intrinsic size of a flex container.
This fixes an infinite recursion when using an intrinsic size keyword as
the max-width of a flex column container.
Note that this behavior is marked as AD-HOC in code comments because
specs don't tell us how to achieve intrinsic sizing.
We can now load product pages on the Twinings site, such as
https://twinings.co.uk/products/earl-grey-100-tea-bags :^)
Fixes the issue that before "automatic minimum size" were used to size
flexible tracks even though specification says is should be "minimum
contribution"
When toggling `display: none` on an element, it can go from having a
layout subtree to not having one. In the `none` case, we were previously
leaving stale layout nodes hanging off DOM nodes in the subtree.
These layout nodes could be queried for outdated information and
probably other things that we shouldn't allow.
Fix this by having TreeBuilder prune any old layout nodes hanging off
nodes in a subtree after its subtree root doesn't produce a layout node.
This is defined in the spec, but was missing in our table. Fix this, and
add a spec comment for what is missing. Also begin a basic text based
test for URL, so we can get some coverage of LibWeb's usage of URL too.
Previously, we always rounded border-widths up when converting them to
device pixels. However, the spec asks us to follow a specific algorithm
to "snap" these values, so that the computed value is snapped.
The difference from before, is that widths of between 0 and 1 device
pixels are rounded up to 1, and and values larger than 1 are rounded
down.
If a math function resolves to `<length>` or `<percentage>`, then it
will by definition also resolve to `<length-percentage>`. (Same for any
other basic types.) Since we were checking `<length-percentage>` first
and then bailing if no given properties could accept that, math
functions would always fail to match a property that just accepts a non
`-percentage` type.
This is a universal value like `initial` and `inherit` and works by
reverting the current value to whatever we had at the start of the
current cascade origin.
The implementation is somewhat inefficient as we make a copy of all
current values at the start of each origin. I'm sure we can come up with
a way to make this faster eventually.
Allow the left margin of a box which creates a block formatting context
to overlap with left floating boxes which are siblings in the document
tree.
Fixes#20233 and the comment layout on https://lobste.rs.
This change makes tree builder omit elements with "display: contents"
from the layout tree during construction. Their child elements are
instead directly appended to the parent element in layout tree.
For malformed tables which only have cells with span greater than 1, the
content sizes for row and column aren't initialized to non-zero values.
Avoid undefined behavior in such cases, which sometimes show up on
Wikipedia.
Auto margins used together with justify-content would previously
result in children being positioned outside their parent. This was
solved by letting auto margins take precedence when they are used,
which was already implemented to some extent before, but not
fully.
Containers with both flex reverse and justify content would
sometimes place children outside the container. This happened
because it assumed any reversed container would have items
aligned to the right, which isn't true when using eg. `flex-end`.
Both `justify-content: start` and `justify-content: end` are now
also independent of the reverseness.
After switching to fixed-point arithmetic in CSSPixels, it no longer
supports representing infinite values, which was previously the case
for remaining_free_space in FFC. Using Optional that is not empty only
when value is finite to store remaining_free_space ensures that
infinity is avoided in layout calculations.
This fixes an assertion on https://amazon.com/ since WindowProxy
would advertise "0" as an own property key, but then act like it was
a bogus property when actually queried for it directly.
Before this change, we always derived a box's baseline from its last
child, even if the last child didn't have any line boxes inside.
This caused baselines to slip further down vertically than expected.
There are more baseline alignment issues to fix, but this one was
responsible for a fair chunk of trouble. :^)
After the CSSPixels implementation evolved from a wrapper of double
to a fixed-point saturated math arithmetic implementation, it makes
sense to have separate tests for it.
Using fixed-point saturated arithmetics for CSSPixels allows to avoid
accumulating floating-point errors.
This implementation is not complete yet: currently saturated
arithmetics implemented only for addition. But it is enough to not
regress any of layout tests we have :)
See https://github.com/SerenityOS/serenity/issues/18566
Follow the computing column measures section of the specification, which
gives an algorithm for setting intrinsic percentage widths when spanning
columns are involved.
Change how we store type of columns. It was used where the specification
only distinguishes between percent and everything else, so it makes more
sense to store and use it as a boolean.
The specification says we should distribute excess width proportionally
to the width of the cell, not to the preferred increment. Doing the
latter leads to distributing all excess width to just the cells which
demand some increment, even if it's very modest. Moreover, there's code
which partially implements the correct criteria just below the one we
remove here.