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.
Instead, perform the filtering for each rule as we go. This avoids
creating a separate list of rules, which was ~5% of runtime when
mousing around on the Discord web interface.
CSSStyleSheet now caches the CSSNamespaceRule for the default namespace,
which is the only one we currently care about. This saves us from
iterating over its list of rules every time we want to know what that
default namespace is.
The spec dictates that `@namespace` rules are only valid near the start
of a stylesheet, so we also take advantage of that to quit searching
for namespaces as soon as we see a non-import rule.
Also renamed `namespace_filter()` to `default_namespace()` since that's
what it actually returns.
This makes github.com/serenityos/serenity snappy again. :^)
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.
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.
Now that we have a way to resolve calc() lengths without a layout node,
we can finally support calc() values in font-size.
This wasn't possible before because font-related properties have to be
resolved eagerly in StyleComputer due to font-relative CSS length units
depending on the computed font being known.
Unlike DOM mutations, CSS animations don't affect the style of the
entire subtree of the element being animated. This means we only have to
recompute style for the animating element, which is significantly
faster than doing the whole subtree.
This takes idle CPU usage on https://shopify.com/ from 100% to 30% on my
(not massively powerful) laptop. :^)
This commit makes the StyleComputer avoid restarting finished animations
(e.g. animations with finite iteration counts that have run to
completion).
As a bonus, it also disables the animation timer when all animations
have finished running.
This partially implements CSS-Animations-1 (though there are references
to CSS-Animations-2).
Current limitations:
- Multi-selector keyframes are not supported.
- Most animation properties are ignored.
- Timing functions are not applied.
- Non-absolute values are not interpolated unless the target is also of
the same non-absolute type (e.g. 10% -> 25%, but not 10% -> 20px).
- The JavaScript interface is left as an exercise for the next poor soul
looking at this code.
With those said, this commit implements:
- Interpolation for most common types
- Proper keyframe resolution (including the synthetic from-keyframe
containing the initial state)
- Properly driven animations, and proper style invalidation
Co-Authored-By: Andreas Kling <kling@serenityos.org>
This allows us to figure out where a specific CSS property comes from,
which is going to be used in a future commit to uniquely identify
running animations.
Specifically, stop letting NumericStyleValues holding `0` from
pretending to hold a Length. The parser is now smart enough that we
don't have to do this. :^)
Introduces incomplete parsing of grid shorthand property. Only
<grid-template> part of syntax is supported for now but it is enough
to significantly improve rendering of websites that use this shorthand
to define grid :)
If CSS requests a font that we have loaded, but we didn't associate it
with a specific weight and/or slope, let's still use it if it matches
the family name.
This is a hack until we implement proper CSS font selection.
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.