Previously, ChunkID's from_big_endian_number() and
as_big_endian_number() weren't inverses of each other.
ChunkID::from_big_endian_number() used to take an u32 that contained
`('f' << 24) | ('t' << 16) | ('y' << 8) | 'p'`, that is
'f', 't', 'y', 'p' in memory on big-endian and 'p', 'y', 't', 'f'
on little-endian, and return a ChunkID for 'f', 't', 'y', 'p'.
ChunkID::as_big_endian_number() used to return an u32 that for a
ChunkID storing 'f', 't', 'y', 'p' was always 'f', 't', 'y', 'p'
in memory on both little-endian and big-endian, that is it stored
`('f' << 24) | ('t' << 16) | ('y' << 8) | 'p'` on big-endian and
`('p' << 24) | ('y' << 16) | ('t' << 8) | 'f'` on little-endian.
`ChunkID::from_big_endian_number(0x11223344).as_big_endian_number()`
returned 0x44332211.
This change makes the two methods self-consistent: they now take
and return a u32 that always has the first ChunkID part in the
highest bits of the u32 (`'f' << 24`), and so on. That also means
they return a u32 that in-memory looks differently on big-endian
and little-endian. Since that's normal for numbers, this also
renames the two methods to just `from_number()` and `to_number()`.
With the semantics cleared up, change the one use in ISOBMFF to read a
BigEndian for chunk headers and brand codes. This has the effect of
tags now being printed in the right order.
Before:
```sh
% Build/lagom/bin/isobmff ~/Downloads/sample1.jp2
Unknown Box (' Pj')
[ 4 bytes ]
('pytf') (version = 0, flags = 0x0)
- major_brand = ' 2pj'
- minor_version = 0
- compatible_brands = { ' 2pj' }
Unknown Box ('h2pj')
[ 37 bytes ]
Unknown Box ('fniu')
[ 92 bytes ]
Unknown Box (' lmx')
[ 2736 bytes ]
Unknown Box ('c2pj')
[ 667336 bytes ]
```
After:
```sh
% Build/lagom/bin/isobmff ~/Downloads/sample1.jp2
hmm 0x11223344 0x11223344
Unknown Box ('jP ')
[ 4 bytes ]
('ftyp' ) (version = 0, flags = 0x0)
- major_brand = 'jp2 '
- minor_version = 0
- compatible_brands = { 'jp2 ' }
Unknown Box ('jp2h')
[ 37 bytes ]
Unknown Box ('uinf')
[ 92 bytes ]
Unknown Box ('xml ')
[ 2736 bytes ]
Unknown Box ('jp2c')
[ 667336 bytes ]
```
This was causing some racey behaviour in LibHTTP, and just generally
lead to really bad stack traces; avoid that by switching to
Core::Promise and using the existing event loop.
Possibly resolves#23524 and #23642.
Given a selector like `.foo .bar #baz`, we know that elements with
the class names `foo` and `bar` must be present in the ancestor chain of
the candidate element, or the selector cannot match.
By keeping track of the current ancestor chain during style computation,
and which strings are used in tag names and attribute names, we can do
a quick check before evaluating the selector itself, to see if all the
required ancestors are present.
The way this works:
1. CSS::Selector now has a cache of up to 8 strings that must be present
in the ancestor chain of a matching element. Note that we actually
store string *hashes*, not the strings themselves.
2. When Document performs a recursive style update, we now push and pop
elements to the ancestor chain stack as they are entered and exited.
3. When entering/exiting an ancestor, StyleComputer collects all the
relevant string hashes from that ancestor element and updates a
counting bloom filter.
4. Before evaluating a selector, we first check if any of the hashes
required by the selector are definitely missing from the ancestor
filter. If so, it cannot be a match, and we reject it immediately.
5. Otherwise, we carry on and evaluate the selector as usual.
I originally tried doing this with a HashMap, but we ended up losing
a huge chunk of the time saved to HashMap instead. As it turns out,
a simple counting bloom filter is way better at handling this.
The cost is a flat 8KB per StyleComputer, and since it's a bloom filter,
false positives are a thing.
This is extremely efficient, and allows us to quickly reject the
majority of selectors on many huge websites.
Some example rejection rates:
- https://amazon.com: 77%
- https://github.com/SerenityOS/serenity: 61%
- https://nytimes.com: 57%
- https://store.steampowered.com: 55%
- https://en.wikipedia.org: 45%
- https://youtube.com: 32%
- https://shopify.com: 25%
This also yields a chunky 37% speedup on StyleBench. :^)
Previously, the invalid value default wasn't taken into account when
determining the value that should be returned from the getter of an
enumerated attribute. This caused a crash when an enumerated attribute
of type DOMString? was set to an invalid value.
I've seen a crash when trying to verify_cast some block-level box to a
BlockContainer when it's actually something else.
This patch adds a debug log message so we can learn more about it next
time it happens somewhere.
Since we drive painting for SVG-as-image manually anyway, there's no
need for them to say they are "ready to paint", since that just causes
unnecessary extra processing in the HTML event loop.
We do the same thing with the gzip utility for performance.
This reduces the runtime of `./bin/base64 enwik8 >/dev/null` from
0.428s to 0.303s.
This reduces the runtime of `./bin/base64 -d enwik8.base64 >/dev/null`
from 0.632s to 0.469s.
(enwik8 is a 100MB test file from http://mattmahoney.net/dc/enwik8.zip)
There's no need to copy the result. We can also avoid increasing the
size of the output buffer by 1 for each written byte.
This reduces the runtime of `./bin/base64 -d enwik8.base64 >/dev/null`
from 0.917s to 0.632s.
(enwik8 is a 100MB test file from http://mattmahoney.net/dc/enwik8.zip)
We don't really need the features provided by StringBuilder here, since
we know the exact size of the output. Avoiding StringBuilder avoids the
recurring capacity/size checks both within StringBuilder itself and its
internal ByteBuffer.
This reduces the runtime of `./bin/base64 enwik8 >/dev/null` from
0.976s to 0.428s.
(enwik8 is a 100MB test file from http://mattmahoney.net/dc/enwik8.zip)
We know we are only appending ASCII characters to the StringBuilder, so
do not bother validating the result.
This reduces the runtime of `./bin/base64 enwik8 >/dev/null` from
1.192s to 0.976s.
(enwik8 is a 100MB test file from http://mattmahoney.net/dc/enwik8.zip)
Instead of invalidating animated style properties whenever
`Document::update_style()` is called, now we only do that when
animations might have actually progressed. We still have to ensure
animated properties are up-to-date in `update_style()` to ensure that
JS methods can access updated style properties.
Before this change, we ran style and layout updates from both event
loop processing and update timers. This could have caused missed resize
observer updates and unnecessary updating of style or layout more than
once before repaint.
Also, we can now be sure unnecessary style or layout updates won't
happen in `EventLoop::spin_processing_tasks_with_source_until()`.
In our implementation of the "apply the history step" algorithm, we
have to spin-wait for the completion of tasks queued on the event loop.
Before this change, we allowed tasks from any source to be executed
while we were waiting. It should not be possible because it allows to
interrupt history step application by anything, including another
history step application.
Fixes https://github.com/SerenityOS/serenity/issues/23598
This encoding scheme comes from section 5 of RFC 4648, as an
alternative to the standard base64 encode/decode methods.
The only difference is that the last two characters are replaced
with '-' and '_', as '+' and '/' are not safe in URLs or filenames.
You can now run
image -o out.png Tests/LibGfx/test-inputs/bmp/bitmap.bmp \
--crop 130,86,108,114
and end up with the nose part of that image in out.png.
This isn't required as the StyleComputer will do this when animating,
but this allows the properties to be resolved once instead of on
every animation frame.
Note that we still pass AllowUnresolved::Yes because the properties will
not be resolved if there is no target.
When iterating through a @keyframes rule, it isn't possible to resolve
unresolved style properties since there are no elements. This change
allows those properties to simply pass through this helper function.
These will need to store unresolved styles as well, since they may be
built during parsing of a @keyframes rule. In that case there is no
target element or pseudo-element, and thus the value cannot be resolved.
structured_deserialize_internal() is added to support sub
deserialization from serializable interfaces serialization steps which
needs the ability to pass onto the current position in the deserialized
data.