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.
SVG presentation attributes are parsed as CSS values, so we also need to
handle CSS variable expansion when handling them.
This (roughly) matches the behavior of other engines. It's also used on
the web, for example on https://stripe.com/ :^)
The resolved property sets are stored with the element in a
per-pseudo-element array (same as for pseudo element layout nodes).
Longer term, we should stop storing this with elements entirely and make
it temporary state in StyleComputer somehow, so we don't waste memory
keeping all the resolved properties around.
This makes various gradients show up on https://shopify.com/ :^)
The root element font metrics were getting queried again and again
during style computation. Before this change we would do some work to
recalculate them each time.
This patch simply caches them in a StyleComputer member. Since style
updates always start with the root element, we know that it'll be
up-to-date by the time we look at any other element.
Before this change, we were spending ~5% of CPU time on Google Groups
in root_element_font_metrics().
This necessitated returning `nullptr` instead of just `{}` in a lot of
places. Also, some temporary hackiness in `parse_css_value()`: That
returns a special `ParseError` type already, so we now have a
`FIXME_TRY()` macro which logs the error and then returns a generic
`ParseError::InternalError` value. Eventually this macro will go away,
once I figure out how to deal with this more nicely.
In particular, we now blockify layout internal boxes (e.g table parts)
by turning them into `block flow`. This fixes a crash when viewing
our GitHub repo :^)
The Display class already supported all specific values, and now they
will be parsed too. The display property now has a special type
DisplayStyleValue.
Rather than passing an increasingly-unwieldy number of font parameters
individually to every function that resolves lengths, let's wrap them
up.
This is frustratingly close to being `Gfx::FontPixelMetrics`, but bitmap
fonts cause issues: We choose the closest font to what the CSS
requests, but that might have a wildly different size than what the
page expects, so we have to fudge the numbers.
No behaviour changes.
When deciding on a box type transformation (blockify/inlinify) for a
pseudo element, we have to use the originating element as a reference
rather than the parent.
(The originating element *is* the parent for its pseudo elements.)
This fixes multi-layer backgrounds with background positions. This
is a little awkard, so maybe it would be better to refactor the
parsing code to make these lists directly, but right now this is
the simplest fix.
This parses the new background-position-x/y longhands and properly
hooks up them up. This requires converting PositionStyleValue to
just contain two EdgeStyleValues so that it can be easily expanded
into the longhands.
Due to CSSImportRule::has_import_result() being backwards, we never
actually entered imported style sheets when traversing style rules or
media queries.
With this fixed, we no longer need the "collect style sheets" step in
StyleComputer, as normal for_each_effective_style_rule() will now
actually find all the rules. :^)
We don't support all parts of the font formats we assume as "supported"
in the CSS parser. For example, if an open type font has a CFF table, we
reject loading it. This meant that until now, when such an
unsupported-supported font url was first in the list of urls, we
couldn't load it at all, even when we would support a later url.
To resolve that, try loading all font urls one after each other, in case
we are not able to load the higher priority one.
This also resolves a FIXME related to spec compliant url prioritization.
Our CSS parser already filters and prioritizes font src urls in
compliance with the spec. However, we still had to resort to brittle
file extension matching, because some websites don't set the `format`
and if the first url in a src list happened to be one we don't support,
the font could not be loaded at all. This now is unnecessary because we
can try and discard the urls instead.
It is not sufficient to just invalidate layout when a new font has
loaded, because while it was loading we might have chosen a fallback
font-family value instead.
Invalidate style instead.
Before this patch, we would build full computed style for these pseudo
elements, for every DOM element, even if no ::before/::after selector
actually matched.
This was a colossal waste of time, and we can also just not do that.
Instead, just abort pseudo element style resolution early if no relevant
selectors matched. :^)
Percentage line-height values are relative to 1em (i.e the font-size
of the element). We have to resolve their computed values before
proceeding with inheritance.
Use FlyString::from_deprecated_fly_string() in these instances instead
of FlyString::from_utf8(). As we convert to new FlyString/String we want
to be aware of these potential unnecessary allocations.
Instead of putting every rule that matches a pseudo element in the
same bucket, let them go in the best ID/class/tag name bucket instead.
Then, add a flag to MatchingRule that says whether it contains a
pseudo element in the rightmost compound selector.
When deciding which selectors to run for an element, we can now simply
filter in/out pseudo element selectors as appropriate depending on what
we're trying to match.
This fixes an issue where pages using Font Awesome had 1700+ rules in the
pseudo-element rule cache. (This meant all those rules had to run
against every element twice or more while instantiating pseudo elements.)
We were already sorting the author style selectors into buckets.
Now we do it for the built-in UA style as well.
This means less work for the selector engine everywhere :^)
This relied on pulling the current realm from the main thread VM, which
requires an execution context to be on the VM's stack. This heavily
relied on the dummy execution context that is always on the stack, for
example, when parsing the UA style sheets where no JavaScript is
running.
This class had slightly confusing semantics and the added weirdness
doesn't seem worth it just so we can say "." instead of "->" when
iterating over a vector of NNRPs.
This patch replaces NonnullRefPtrVector<T> with Vector<NNRP<T>>.
DeprecatedFlyString relies heavily on DeprecatedString's StringImpl, so
let's rename it to A) match the name of DeprecatedString, B) write a new
FlyString class that is tied to String.
Previously when resolving an attr or var-defined property
with a 'not-set' value like this `property: var(--ValueNotSet)`,
we left the property unchanged (as an unresolved) and
added it to the computed-style of the element.
We still don't change the property but rather we now also don't set
unresolved properties in the computed-style.
This is an intended behavior.
The specification suggests that, on resolving an attr or var property
(custom properties) we have an invalid property when neither the
variable inside the var, nor the backup value could be resolved.
An invalid property must be inherited or defaulted depending on it's
type. We already do this with every 'untouched'
(as in m_property_values contains no entry for it) value.
So not setting the property results in an inherited (or initial)
value by a later-called function.
This also fixes another problem, where
`text-decoration: var(--NotSet)`
wouldn't be inherited because the computed-style of the
parent element hasn't set `text-decoration` but rather
all it's long-versions like `text-decoration-line` and so on.
This will make it easier to support both string types at the same time
while we convert code, and tracking down remaining uses.
One big exception is Value::to_string() in LibJS, where the name is
dictated by the ToString AO.
We have a new, improved string type coming up in AK (OOM aware, no null
state), and while it's going to use UTF-8, the name UTF8String is a
mouthful - so let's free up the String name by renaming the existing
class.
Making the old one have an annoying name will hopefully also help with
quick adoption :^)
When mixing calc() and var(), we're forced to delay resolving them until
we're in StyleComputer. Previously we'd just skip over them.
This patch handles calc() in the same pass as attr(). We reparse the
calc() value after var() expansion, and then try to resolve it to a
constant value if possible. If it's not possible, we leave the calc()
where it is, and maybe layout can figure it out later.
Note that I've only implemented resolution to integer and percentage in
this commit. There are more things a calc() could resolve to, and we
should implement those as well.
We always create a Layout::InitialContainingBlock for the ICB, but in a
future where we always honor the CSS::Display everywhere, we need to
make sure everyone has the right display values.
URL had properly named replacements for protocol(), set_protocol() and
create_with_file_protocol() already. This patch removes these function
and updates all call sites to use the functions named according to the
specification.
See https://url.spec.whatwg.org/#concept-url-scheme
Instead of asking Gfx::FontDatabase for the "default font" and the
"default fixed-width font", we now proxy those requests out via
the Platform::FontPlugin. This will allow Ladybird to use other default
fonts as fallback.
This patch improves @font-face loading when there are multiple src
values in two ways:
- Invalid/empty URLs are ignored
- Fonts with unsupported file extensions are ignored
This makes us load and display the emblem font on modern Reddit,
which is pretty neat! :^)
Let's make 16px the default font size instead of 10px. This makes our
layout results match those of other engines in many more cases.
Also make the h1-h6 element styles use relative (em) font sizes, also
matching other browsers.
Instead of hard-coding the names of system fonts to use for the CSS
generic fonts (like "sans-serif", "monospace", etc.) we now call out
to a Platform::FontPlugin and ask for the generic names.
Previously, `inline-flex` would blockify to `block` since blockification
didn't take the inner display type into account. This is still not
perfect, but it fixes a lot of situations where inline-level flex
containers would be demoted to regular block containers.
Each of these strings would previously rely on StringView's char const*
constructor overload, which would call __builtin_strlen on the string.
Since we now have operator ""sv, we can replace these with much simpler
versions. This opens the door to being able to remove
StringView(char const*).
No functional changes.
By the time that property() gets called, we've already given every
single property a value, so we can just return it. This simplifies a
lot of places that were manually handling a lack of value
unnecessarily.
This means deviating a little from the spec, so that we create a
complete Block in one go instead of creating an empty one and then
poking at its internals.
The goal here is to move the parser-internal classes into this namespace
so they can have more convenient names without causing collisions. The
Parser itself won't collide, and would be more convenient to just
remain `CSS::Parser`, but having a namespace and a class with the same
name makes C++ unhappy.
Previously, we would create a new Gfx::ScaledFont whenever we needed one
for an element's computed style. This worked fine on Acid3 since the use
of web fonts was extremely limited.
In the wild, web fonts obviously get used a lot more, so let's have a
per-point-size font cache for them.
Previously, we were running the "load fonts if needed" machine at the
start of every style computation. That was a lot of unnecessary work,
especially on sites with lots of style rules, since we had to traverse
every style sheet to see if any @font-face rules needed loading.
With this patch, we now load fonts once per sheet, right after adding
it to a document's style sheet list.
"Component value" is the term used in the spec, and it doesn't conflict
with any other types, so let's use the shorter name. :^)
Also, this doesn't need to be friends with the Parser any more.
CSS Values and Units Module Level 5 defines attr as:
`attr(<q-name> <attr-type>?, <declaration-value>?)`
This implementation does not contain support for the type argument,
effectively supporting `attr(<q-name>, <declaration-value>?)`
When encountering a @font-face rule, StyleComputer will now fire off
a resource request and download the first source URL specified.
Once downloaded, we try to parse it as a TrueType font file, and if it
works, it's added to a cache in StyleComputer. This effectively makes
fonts per-document since every document has its own StyleComputer.
This is very unoptimized and could definitely use some caching, etc.
But it does work on Acid3. :^)
Our font database uses point sizes for fonts, and we were passing it
px sizes. This caused all fonts to be 1.333x larger than they should
be on the web. Of course it wasn't always noticeable with bitmap fonts,
but noticeable everywhere with scalable fonts.
Relative font-sizes like "2em" were previously resolved against the
fallback value (10px) which led to incorrect layouts in many places.
Fix this by resolving relative font-sizes against the absolutized
font-size of the parent or root element as appropriate.
Let's make it very clear that these are *computed* values, and not at
all the specified values. The specified values are currently discarded
by the CSS cascade algorithm.
Get rid of the old, roundabout way of invalidating the rule cache by
incrementing the StyleSheetList "generation".
Instead, when something wants to invalidate the rule cache, just have it
directly invalidate the rule cache. This makes it much easier to see
what's happening anyway.
Style computation always happens *before* layout, so we can't rely on
things having (or not having) layout nodes, as that information will
always be one step behind.
Instead, we have to use the DOM to find all the information we need.
Previously we were making a copy of the full set of custom properties
that applied to a DOM element. This was very costly and dominated the
profile when mousing around on GitHub.
Note that this may break custom properties on pseudo elements a little
bit, and that's something we'll have to look into.
Previously this queried the root layout-node's font, which was both
wrong and could crash if the layout wasn't ready yet. Now, it's just
wrong. But at least all the font-related wrongness is grouped together
in StyleComputer. :^)