This isn't directly in the spec, but since replaceChild is implemented
in terms of remove + insert, the removal step may cause arbitrary code
to execute, and so we have to verify that the replaceChild inputs still
make sense afterwards, before doing the insertion.
This roughly matches what WebKit does, and makes a bunch of HTML parsing
tests in WPT stop asserting.
See previous the commit description for more details about the floating
points operations.
The hwb test cases in `css-color-functions` are now rendered identically
to what firefox does (I haven't checked the others tests, but they
aren't affected by this commit).
TL;DR: There are two available sets of coefficients for the conversion
matrices from XYZ to sRGB. We switched from one set to the other, which
is what the WPT tests are expecting.
All RGB color spaces, like display-p3 or rec2020, are defined by their
three color chromacities and a white point. This is also the case for
the video color space Rec. 709, from which the sRGB color space is
derived. The sRGB specification is however a bit different.
In 1996, when formalizing the sRGB spec the authors published a draft
that is still available here [1]. In this document, they also provide
the matrix to convert from the XYZ color space to sRGB. This matrix can
be verified quite easily by using the usual math equations. But hold on,
here come the plot twist: at the time of publication, the spec contained
a different matrix than the one in the draft (the spec is obviously
behind a pay wall, but the numbers are also reported in this official
document [2]). This official matrix, is at a first glance simply a
wrongly rounded version of the one in the draft publication. It however
has some interesting properties: it can be inverted twice (so a
roundtrip) in 8 bits and not suffer from any errors from the
calculations.
So, we are here with two versions of the XYZ -> sRGB matrix, the one
from the spec, which is:
- better for computations in 8 bits,
- and official. This is the one that, by authority, we should use.
And a second version, that can be found in the draft, which:
- makes sense, as directly derived from the chromacities,
- is publicly available,
- and (thus?) used in most places.
The old coefficients were the one from the spec, this commit change them
for the one derived from the mathematical formulae. The Python script to
compute these values is available at the end of the commit description.
More details about this subject can be found here [3].
[1] https://www.w3.org/Graphics/Color/sRGB.html
[2] https://color.org/chardata/rgb/sRGB.pdf
[3] https://photosauce.net/blog/post/making-a-minimal-srgb-icc-profile-part-3-choose-your-colors-carefully
The Python script:
```python
# http://www.brucelindbloom.com/index.html?Eqn_RGB_XYZ_Matrix.html
from numpy.typing import NDArray
import numpy as np
### sRGB
# https://www.w3.org/TR/css-color-4/#predefined-sRGB
srgb_r_chromacity = np.array([0.640, 0.330])
srgb_g_chromacity = np.array([0.300, 0.600])
srgb_b_chromacity = np.array([0.150, 0.060])
##
## White points
white_point_d50 = np.array([0.345700, 0.358500])
white_point_d65 = np.array([0.312700, 0.329000])
#
r_chromacity = srgb_r_chromacity
g_chromacity = srgb_g_chromacity
b_chromacity = srgb_b_chromacity
white_point = white_point_d65
def tristmimulus_vector(chromacity: NDArray) -> NDArray:
return np.array([
chromacity[0] /chromacity[1],
1,
(1 - chromacity[0] - chromacity[1]) / chromacity[1]
])
tristmimulus_matrix = np.hstack((
tristmimulus_vector(r_chromacity).reshape(3, 1),
tristmimulus_vector(g_chromacity).reshape(3, 1),
tristmimulus_vector(b_chromacity).reshape(3, 1),
))
scaling_factors = (np.linalg.inv(tristmimulus_matrix) @
tristmimulus_vector(white_point))
M = tristmimulus_matrix * scaling_factors
np.set_printoptions(formatter={'float_kind':'{:.6f}'.format})
xyz_65_to_srgb = np.linalg.inv(M)
# http://www.brucelindbloom.com/index.html?Eqn_ChromAdapt.html
# Let's convert from D50 to D65 using the Bradford method.
m_a = np.array([
[0.8951000, 0.2664000, -0.1614000],
[-0.7502000, 1.7135000, 0.0367000],
[0.0389000, -0.0685000, 1.0296000]
])
cone_response_source = m_a @ tristmimulus_vector(white_point_d50)
cone_response_destination = m_a @ tristmimulus_vector(white_point_d65)
cone_response_ratio = cone_response_destination / cone_response_source
m = np.linalg.inv(m_a) @ np.diagflat(cone_response_ratio) @ m_a
D50_to_D65 = m
xyz_50_to_srgb = xyz_65_to_srgb @ D50_to_D65
print(xyz_50_to_srgb)
print(xyz_65_to_srgb)
```
That makes us pass the following WPT tests:
- css/css-color/srgb-linear-001.html
- css/css-color/srgb-linear-002.html
- css/css-color/srgb-linear-003.html
After we absolutize the contents of :has(), we check that those child
selectors don't contain anything that :has() rejects.
This is a separate path than the checks inside the parser, which is
unfortunate.
Fixes a WPT ref test. :^)
Attempt 2! Reverts 2a5dbedad4
This time, set up a different combinator when producing a relative
invalid selector rather than a standalone one. This fixes the crash.
Original description below for simplicity because it still applies.
---
Selectors like `:is(.valid, &!?!?!invalid)` need to keep the invalid
part around, even though it will never match, for a couple of reasons:
- Serialization needs to include them
- For nesting, we care if a `&` appeared anywhere in the selector, even
in an invalid part.
So this patch introduces an `Invalid` simple selector type, which simply
holds its original ComponentValues. We search through these looking for
`&`, and we dump them out directly when asked to serialize.
Selectors like `:is(.valid, &!?!?!invalid)` need to keep the invalid
part around, even though it will never match, for a couple of reasons:
- Serialization needs to include them
- For nesting, we care if a `&` appeared anywhere in the selector, even
in an invalid part.
So this patch introduces an `Invalid` simple selector type, which simply
holds its original ComponentValues. We search through these looking for
`&`, and we dump them out directly when asked to serialize.
A few are skipped for now:
- A few ref tests fail
- Crash tests are not supported by our runner and time out
- top-level-is-scope.html crashes and needs further investigation
This causes 36 new subtests to pass locally. :^)
Unfortunately at least one of these is flaky when it's able to load the
font file, apparently because we don't wait for the font and its
stylesheet to actually load before the tests run.
Eventually we want to stop skipping these, so it's helpful to know why
they were skipped in the first place. :^)
I've grouped them together by reason, so the order has changed a little.
For some of these the reason isn't clear.
This fixes structured serialization of DataView. It was expected
to be uniform with TypedArray, which returns u32 for byte_offset().
This was covered by a number of WPT infrastructure tests, which this
commit also imports.
It's actually possible for there to be no adjusted current node, when
the stack of open elements is empty. This was covered by one of the WPT
parsing tests.
The spec expects `postMessage()` to act as if it is invoked
immediately. Since `postMessage()` isn't actually invoked immediately,
keep tasks with source `PostedMessage` in the task queue, so that these
tasks are processed. Fixes a hang when `WorkerGlobalScope.close()` is
called immediately after `postMessage()`.