@miaskiewicz/turbo-dom 0.7.0 → 0.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/Cargo.lock +7 -7
- package/Cargo.toml +7 -5
- package/README.md +13 -8
- package/package.json +1 -1
- package/pkg/package.json +2 -2
- package/pkg/turbo_dom_parser_bg.wasm +0 -0
- package/pkg-web/package.json +2 -2
- package/pkg-web/turbo_dom_parser_bg.wasm +0 -0
- package/src/runtime/cascade.mjs +70 -54
- package/src/runtime/css-shorthand.mjs +1217 -734
- package/src/runtime/custom-elements.mjs +8 -2
- package/src/runtime/dom.mjs +148 -95
- package/src/runtime/events.mjs +106 -85
- package/src/runtime/html-serialize.mjs +28 -24
- package/src/runtime/selectors.mjs +353 -237
- package/src/runtime/stubs.mjs +9 -6
- package/turbo-dom-parser.darwin-arm64.node +0 -0
- package/turbo-dom-parser.linux-arm64-gnu.node +0 -0
- package/turbo-dom-parser.linux-x64-gnu.node +0 -0
- package/turbo-dom-parser.linux-x64-musl.node +0 -0
- package/turbo-dom-parser.win32-x64-msvc.node +0 -0
package/Cargo.lock
CHANGED
|
@@ -2791,7 +2791,7 @@ dependencies = [
|
|
|
2791
2791
|
|
|
2792
2792
|
[[package]]
|
|
2793
2793
|
name = "turbo-dom"
|
|
2794
|
-
version = "0.
|
|
2794
|
+
version = "0.8.0"
|
|
2795
2795
|
dependencies = [
|
|
2796
2796
|
"cssparser",
|
|
2797
2797
|
"html5ever",
|
|
@@ -2807,7 +2807,7 @@ dependencies = [
|
|
|
2807
2807
|
|
|
2808
2808
|
[[package]]
|
|
2809
2809
|
name = "turbo-dom-browser-env"
|
|
2810
|
-
version = "0.
|
|
2810
|
+
version = "0.8.0"
|
|
2811
2811
|
dependencies = [
|
|
2812
2812
|
"cssparser",
|
|
2813
2813
|
"lightningcss",
|
|
@@ -2825,7 +2825,7 @@ dependencies = [
|
|
|
2825
2825
|
|
|
2826
2826
|
[[package]]
|
|
2827
2827
|
name = "turbo-dom-layout"
|
|
2828
|
-
version = "0.
|
|
2828
|
+
version = "0.8.0"
|
|
2829
2829
|
dependencies = [
|
|
2830
2830
|
"core-foundation",
|
|
2831
2831
|
"core-text",
|
|
@@ -2849,7 +2849,7 @@ dependencies = [
|
|
|
2849
2849
|
|
|
2850
2850
|
[[package]]
|
|
2851
2851
|
name = "turbo-dom-parser"
|
|
2852
|
-
version = "0.
|
|
2852
|
+
version = "0.8.0"
|
|
2853
2853
|
dependencies = [
|
|
2854
2854
|
"html5ever",
|
|
2855
2855
|
"markup5ever_rcdom",
|
|
@@ -2865,7 +2865,7 @@ dependencies = [
|
|
|
2865
2865
|
|
|
2866
2866
|
[[package]]
|
|
2867
2867
|
name = "turbo-dom-raster"
|
|
2868
|
-
version = "0.
|
|
2868
|
+
version = "0.8.0"
|
|
2869
2869
|
dependencies = [
|
|
2870
2870
|
"base64",
|
|
2871
2871
|
"core-foundation",
|
|
@@ -2888,8 +2888,8 @@ dependencies = [
|
|
|
2888
2888
|
|
|
2889
2889
|
[[package]]
|
|
2890
2890
|
name = "turbo-dom-v8"
|
|
2891
|
-
version = "150.4.
|
|
2892
|
-
source = "git+https://github.com/miaskiewicz/turbo-dom?rev=
|
|
2891
|
+
version = "150.4.1"
|
|
2892
|
+
source = "git+https://github.com/miaskiewicz/turbo-dom?rev=a673f79e22006b85315670493800418932f2df31#a673f79e22006b85315670493800418932f2df31"
|
|
2893
2893
|
dependencies = [
|
|
2894
2894
|
"bindgen",
|
|
2895
2895
|
"bitflags 2.12.1",
|
package/Cargo.toml
CHANGED
|
@@ -43,7 +43,7 @@ manual_midpoint = "allow"
|
|
|
43
43
|
|
|
44
44
|
[package]
|
|
45
45
|
name = "turbo-dom-parser"
|
|
46
|
-
version = "0.
|
|
46
|
+
version = "0.8.0"
|
|
47
47
|
edition = "2021"
|
|
48
48
|
description = "html5ever-backed native parser for turbo-dom (Layer 1)"
|
|
49
49
|
# Ships to npm only (napi/wasm addon); never published to crates.io.
|
|
@@ -114,8 +114,10 @@ opt-level = 3
|
|
|
114
114
|
inherits = "release"
|
|
115
115
|
debug = true
|
|
116
116
|
|
|
117
|
-
# Pre-publish only: resolve the
|
|
118
|
-
# (crates/turbo-dom-v8/scripts/push-candidate.sh
|
|
119
|
-
#
|
|
117
|
+
# Pre-publish only: resolve the unpublished turbo-dom-v8 150.4.1 (pins.json) from its
|
|
118
|
+
# candidate branch (crates/turbo-dom-v8/scripts/push-candidate.sh prints the rev). crates.io
|
|
119
|
+
# has 150.4.0; publish.yml publishes 150.4.1 first and then drops this table and re-locks
|
|
120
|
+
# from crates.io (`scripts/publish-crates.py drop-v8-patch`, PUBLISHING.md). Re-point it
|
|
121
|
+
# after every reviewed fork change; check-versions.py asserts the locked version.
|
|
120
122
|
[patch.crates-io]
|
|
121
|
-
turbo-dom-v8 = { git = "https://github.com/miaskiewicz/turbo-dom", rev = "
|
|
123
|
+
turbo-dom-v8 = { git = "https://github.com/miaskiewicz/turbo-dom", rev = "a673f79e22006b85315670493800418932f2df31" }
|
package/README.md
CHANGED
|
@@ -1,11 +1,13 @@
|
|
|
1
1
|
# turbo-dom
|
|
2
2
|
|
|
3
|
-
Release **0.
|
|
3
|
+
Release **0.8.0**. turbo-dom is the shared browser-engine base of three projects:
|
|
4
4
|
|
|
5
5
|
- **[turbo-test](https://github.com/miaskiewicz/turbo-test)**, a vitest-compatible test runner. It runs tests in V8 over this repository's DOM.
|
|
6
6
|
- **[turbo-html2pdf](https://github.com/miaskiewicz/turbo-html2pdf)**, which renders HTML to PDF and uses this repository's layout engine.
|
|
7
7
|
- **[turbo-surf](https://github.com/miaskiewicz/turbo-surf)**, a headless browser that uses the DOM, the V8 binding, the layout engine and the screenshot painter.
|
|
8
8
|
|
|
9
|
+
> Agents: start at [docs/AGENTS.md](docs/AGENTS.md) for the one-command workflows in `scripts/agent/`.
|
|
10
|
+
|
|
9
11
|
turbo-dom is also a standalone npm package: a fast, spec-correct DOM environment for
|
|
10
12
|
**vitest** and **jest**, usable in place of jsdom or happy-dom.
|
|
11
13
|
|
|
@@ -24,7 +26,7 @@ and the publish order.
|
|
|
24
26
|
| [`turbo-dom-layout`](crates/turbo-dom-layout) | crates.io | 0.7.0 | HTML/CSS layout (block / inline / flex / grid / table), fonts, images, CSSOM-View geometry | turbo-html2pdf, turbo-surf |
|
|
25
27
|
| [`turbo-dom-raster`](crates/turbo-dom-raster) | crates.io | 0.7.0 | synthetic screenshots (PNG / SVG), canvas op-log replay, WebGL readback | turbo-surf |
|
|
26
28
|
| [`turbo-dom-browser-env`](crates/turbo-dom-browser-env) | crates.io | 0.7.0 | the `rtdom` <-> V8 browser environment (window, document, CSSOM, forms, events) | turbo-test, turbo-surf |
|
|
27
|
-
| [`turbo-dom-v8`](crates/turbo-dom-v8) | crates.io | 150.4.
|
|
29
|
+
| [`turbo-dom-v8`](crates/turbo-dom-v8) | crates.io | 150.4.1 | turbo-dom's rusty_v8 fork, generated from the upstream crate plus reviewed patches | turbo-dom-browser-env, turbo-surf |
|
|
28
30
|
|
|
29
31
|
The root `turbo-dom-parser` crate is `publish = false`. It ships inside the npm package.
|
|
30
32
|
|
|
@@ -105,10 +107,10 @@ parseBuffer, parseFragment })` or set it as `globalThis.__TURBO_DOM_PARSER__`. T
|
|
|
105
107
|
|
|
106
108
|
```toml
|
|
107
109
|
[dependencies]
|
|
108
|
-
turbo-dom = "0.
|
|
109
|
-
turbo-dom-layout = "0.
|
|
110
|
-
turbo-dom-raster = "0.
|
|
111
|
-
turbo-dom-browser-env = "0.
|
|
110
|
+
turbo-dom = "0.8"
|
|
111
|
+
turbo-dom-layout = "0.8" # bundled fonts on by default
|
|
112
|
+
turbo-dom-raster = "0.8"
|
|
113
|
+
turbo-dom-browser-env = "0.8" # needs a V8 archive, see its README
|
|
112
114
|
```
|
|
113
115
|
|
|
114
116
|
```rust
|
|
@@ -134,6 +136,7 @@ Each crate's README covers its API: [turbo-dom](crates/turbo-dom/README.md),
|
|
|
134
136
|
|---|---|---|---|
|
|
135
137
|
| `turbo-dom-layout` | `bundled-fonts` | on | embeds OFL faces (Inter/Roboto, Liberation Serif/PT Serif, Fira Code/IBM Plex Mono) so a document lays out with no caller fonts |
|
|
136
138
|
| | `hyphenation` | on | `hyphens: auto` for English (`en` / `en-*`, not en-GB) with the embedded en-US TeX patterns (~90 KB); off, `auto` acts as `manual` |
|
|
139
|
+
| | `hyphenation-languages` | on | `hyphens: auto` in every other language Chrome 156 on macOS hyphenates (en-GB, ca, cs, da, de, el, es, fi, fr, hr, hu, it, nb / no, nl, pl, pt, ro, ru, sk, sv, uk) with the TeX patterns (~3 MB, all of the `hyphenation` crate's); implies `hyphenation` |
|
|
137
140
|
| | `svg` | off | routes `image/svg+xml` through the SVG rasterizer in the image pipeline (implies `svg-rasterize`) |
|
|
138
141
|
| | `svg-rasterize` | off | the shared resvg rasterizer only (`svg::rasterize`) |
|
|
139
142
|
| | `serde` | off | `Serialize` for the CSSOM-View metrics records |
|
|
@@ -170,8 +173,10 @@ upstream rusty_v8's features (for example `simdutf`).
|
|
|
170
173
|
- **Open gaps (tracked as issues):**
|
|
171
174
|
- glyph stems in raster screenshots lack Chrome's stem darkening, so glyph edges are
|
|
172
175
|
up to ~0.5px thinner per side (#266).
|
|
173
|
-
- Known difference, not tracked: `hyphens: auto`
|
|
174
|
-
|
|
176
|
+
- Known difference, not tracked: `hyphens: auto` breaks at the TeX patterns' points
|
|
177
|
+
(what Chrome uses on Linux, Windows and Android); Chrome on macOS hyphenates with the
|
|
178
|
+
system dictionary, whose points differ for a few words (en `international`, pl
|
|
179
|
+
`szczególnie`; `tests/fixtures/hyphens/words-chrome156.json` lists them).
|
|
175
180
|
- **The npm runtime has no layout.** Geometry is synthetic: `getBoundingClientRect()`,
|
|
176
181
|
`offsetWidth/Height`, `clientWidth/Height` and `scrollWidth/Height` return a non-zero
|
|
177
182
|
size that is stable for a given DOM state (blocks fill the parent's width, the root
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@miaskiewicz/turbo-dom",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.8.0",
|
|
4
4
|
"description": "Faster, more spec-correct DOM for test runners \u2014 native html5ever (Rust/WASM) parser + lazy copy-on-write DOM. A drop-in-style alternative to jsdom/happy-dom for vitest & jest.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"main": "index.js",
|
package/pkg/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "turbo-dom-parser",
|
|
3
3
|
"description": "html5ever-backed native parser for turbo-dom (Layer 1)",
|
|
4
|
-
"version": "0.
|
|
4
|
+
"version": "0.8.0",
|
|
5
5
|
"files": [
|
|
6
6
|
"turbo_dom_parser_bg.wasm",
|
|
7
7
|
"turbo_dom_parser.js",
|
|
@@ -9,4 +9,4 @@
|
|
|
9
9
|
],
|
|
10
10
|
"main": "turbo_dom_parser.js",
|
|
11
11
|
"types": "turbo_dom_parser.d.ts"
|
|
12
|
-
}
|
|
12
|
+
}
|
|
Binary file
|
package/pkg-web/package.json
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"name": "turbo-dom-parser",
|
|
3
3
|
"type": "module",
|
|
4
4
|
"description": "html5ever-backed native parser for turbo-dom (Layer 1)",
|
|
5
|
-
"version": "0.
|
|
5
|
+
"version": "0.8.0",
|
|
6
6
|
"files": [
|
|
7
7
|
"turbo_dom_parser_bg.wasm",
|
|
8
8
|
"turbo_dom_parser.js",
|
|
@@ -13,4 +13,4 @@
|
|
|
13
13
|
"sideEffects": [
|
|
14
14
|
"./snippets/*"
|
|
15
15
|
]
|
|
16
|
-
}
|
|
16
|
+
}
|
|
Binary file
|
package/src/runtime/cascade.mjs
CHANGED
|
@@ -45,25 +45,31 @@ const BORDER_STYLES = new Set(['none', 'hidden', 'dotted', 'dashed', 'solid', 'd
|
|
|
45
45
|
// declarations of the same longhand override naturally (cascade order preserved).
|
|
46
46
|
function setProp(map, name, val) {
|
|
47
47
|
map.set(name, val);
|
|
48
|
-
if (name === 'margin' || name === 'padding')
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
map.set(name + '-top', top); map.set(name + '-right', right);
|
|
52
|
-
map.set(name + '-bottom', bottom); map.set(name + '-left', left);
|
|
53
|
-
} else if (name === 'border') {
|
|
54
|
-
let width, style, color;
|
|
55
|
-
for (const p of val.trim().split(/\s+/)) {
|
|
56
|
-
if (BORDER_STYLES.has(p)) style = p;
|
|
57
|
-
else if (/^(thin|medium|thick)$/.test(p) || /^[\d.]+(px|em|rem|%|pt|vh|vw)?$/.test(p)) width = p;
|
|
58
|
-
else color = p;
|
|
59
|
-
}
|
|
60
|
-
if (width !== undefined) { map.set('border-width', width); for (const s of ['top', 'right', 'bottom', 'left']) map.set(`border-${s}-width`, width); }
|
|
61
|
-
if (style !== undefined) map.set('border-style', style);
|
|
62
|
-
if (color !== undefined) map.set('border-color', color);
|
|
63
|
-
} else if (name === 'background' && !/\s/.test(val.trim())) {
|
|
48
|
+
if (name === 'margin' || name === 'padding') setBoxSides(map, name, val);
|
|
49
|
+
else if (name === 'border') setBorder(map, val);
|
|
50
|
+
else if (name === 'background' && !/\s/.test(val.trim())) {
|
|
64
51
|
map.set('background-color', val.trim());
|
|
65
52
|
}
|
|
66
53
|
}
|
|
54
|
+
// `margin` / `padding`: the 1–4 value side expansion.
|
|
55
|
+
function setBoxSides(map, name, val) {
|
|
56
|
+
const t = val.trim().split(/\s+/);
|
|
57
|
+
const top = t[0], right = t[1] ?? top, bottom = t[2] ?? top, left = t[3] ?? right;
|
|
58
|
+
map.set(name + '-top', top); map.set(name + '-right', right);
|
|
59
|
+
map.set(name + '-bottom', bottom); map.set(name + '-left', left);
|
|
60
|
+
}
|
|
61
|
+
// `border`: width / style / color by the shape of each value.
|
|
62
|
+
function setBorder(map, val) {
|
|
63
|
+
let width, style, color;
|
|
64
|
+
for (const p of val.trim().split(/\s+/)) {
|
|
65
|
+
if (BORDER_STYLES.has(p)) style = p;
|
|
66
|
+
else if (/^(thin|medium|thick)$/.test(p) || /^[\d.]+(px|em|rem|%|pt|vh|vw)?$/.test(p)) width = p;
|
|
67
|
+
else color = p;
|
|
68
|
+
}
|
|
69
|
+
if (width !== undefined) { map.set('border-width', width); for (const s of ['top', 'right', 'bottom', 'left']) map.set(`border-${s}-width`, width); }
|
|
70
|
+
if (style !== undefined) map.set('border-style', style);
|
|
71
|
+
if (color !== undefined) map.set('border-color', color);
|
|
72
|
+
}
|
|
67
73
|
|
|
68
74
|
function parseDecls(text, into) {
|
|
69
75
|
const map = into || new Map();
|
|
@@ -95,21 +101,30 @@ function parseStylesheet(css, startOrder, rules) {
|
|
|
95
101
|
if (css[j] === ';') { order++; i = j + 1; continue; }
|
|
96
102
|
if (css[j] === '}') { i = j + 1; continue; }
|
|
97
103
|
const sel = css.slice(i, j).trim();
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
const decls = parseDecls(css.slice(j + 1, k - 1));
|
|
102
|
-
// the prelude is a selector LIST split at top-level commas only (`:is(a, b)` stays
|
|
103
|
-
// whole); an invalid list drops the whole rule, as in a browser; each item carries its
|
|
104
|
-
// Selectors 4 specificity (mirrors rtdom::cascade::parse_stylesheet)
|
|
105
|
-
if (decls.size) for (const t of ruleSelectors(sel)) {
|
|
106
|
-
rules.push({ selector: t, decls, spec: specificity(t), order: order++ });
|
|
107
|
-
}
|
|
108
|
-
} else { order++; }
|
|
104
|
+
const k = blockEnd(css, j + 1);
|
|
105
|
+
if (sel && sel[0] !== '@') order = pushRules(sel, parseDecls(css.slice(j + 1, k - 1)), order, rules);
|
|
106
|
+
else order++;
|
|
109
107
|
i = k;
|
|
110
108
|
}
|
|
111
109
|
return order;
|
|
112
110
|
}
|
|
111
|
+
// The index just past the `}` closing the block whose body starts at `k` (the input's
|
|
112
|
+
// end when unclosed).
|
|
113
|
+
function blockEnd(css, k) {
|
|
114
|
+
let depth = 1;
|
|
115
|
+
while (k < css.length && depth > 0) { const ch = css[k]; if (ch === '{') depth++; else if (ch === '}') depth--; k++; }
|
|
116
|
+
return k;
|
|
117
|
+
}
|
|
118
|
+
// The rules of a style rule with prelude `sel`; returns the next source order.
|
|
119
|
+
function pushRules(sel, decls, order, rules) {
|
|
120
|
+
// the prelude is a selector LIST split at top-level commas only (`:is(a, b)` stays
|
|
121
|
+
// whole); an invalid list drops the whole rule, as in a browser; each item carries its
|
|
122
|
+
// Selectors 4 specificity (mirrors rtdom::cascade::parse_stylesheet)
|
|
123
|
+
if (decls.size) for (const t of ruleSelectors(sel)) {
|
|
124
|
+
rules.push({ selector: t, decls, spec: specificity(t), order: order++ });
|
|
125
|
+
}
|
|
126
|
+
return order;
|
|
127
|
+
}
|
|
113
128
|
|
|
114
129
|
function buildIndex(scope) {
|
|
115
130
|
// scope is always a Document or ShadowRoot — both expose getElementsByTagName.
|
|
@@ -225,6 +240,29 @@ function makeProxy(map, v) {
|
|
|
225
240
|
});
|
|
226
241
|
}
|
|
227
242
|
|
|
243
|
+
// Encapsulation: an element inside a shadow tree resolves against that shadow root's own
|
|
244
|
+
// <style> rules, not the document's (and vice-versa). Gated on __hasShadow so the
|
|
245
|
+
// no-shadow path keeps resolving against doc.
|
|
246
|
+
function styleScope(doc, el) {
|
|
247
|
+
if (!doc.__hasShadow) return doc;
|
|
248
|
+
const root = el.getRootNode && el.getRootNode();
|
|
249
|
+
return root && root.__isShadowRoot ? root : doc;
|
|
250
|
+
}
|
|
251
|
+
// The shadow overlays (only once the document has a shadow root).
|
|
252
|
+
function collectShadowMatched(el, v, matched) {
|
|
253
|
+
// shadow host: overlay :host rules from its OWN shadow root's stylesheet
|
|
254
|
+
if (el.__shadow) collectMatched(el, getIndex(el.__shadow, v), 'host', matched);
|
|
255
|
+
// slotted light node: overlay ::slotted rules from the slot's shadow root
|
|
256
|
+
const slot = el.assignedSlot;
|
|
257
|
+
if (slot) { const sr = slot.getRootNode(); if (sr.__isShadowRoot) collectMatched(el, getIndex(sr, v), 'slotted', matched, slot); }
|
|
258
|
+
}
|
|
259
|
+
// The unset INHERITED properties of `map` from the parent's computed style `ps`.
|
|
260
|
+
function inheritFrom(map, ps) {
|
|
261
|
+
for (const prop of INHERITED) {
|
|
262
|
+
if (!map.has(prop)) { const pv = ps.getPropertyValue(prop); if (pv) map.set(prop, pv); }
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
|
|
228
266
|
// getComputedStyle(el): resolves cascade, memoized per element on the current
|
|
229
267
|
// Document.__version (any style/DOM mutation bumps it → cache auto-invalidates).
|
|
230
268
|
export function makeGetComputedStyle() {
|
|
@@ -238,24 +276,9 @@ export function makeGetComputedStyle() {
|
|
|
238
276
|
const map = new Map();
|
|
239
277
|
if (!doc) { const p = makeProxy(map, v); el.__computedStyle = p; return p; }
|
|
240
278
|
|
|
241
|
-
// Encapsulation: an element inside a shadow tree resolves against that
|
|
242
|
-
// shadow root's own <style> rules, not the document's (and vice-versa).
|
|
243
|
-
// Gated on __hasShadow so the no-shadow path keeps resolving against doc.
|
|
244
|
-
let scope = doc, inShadow = false;
|
|
245
|
-
if (doc.__hasShadow) {
|
|
246
|
-
const root = el.getRootNode && el.getRootNode();
|
|
247
|
-
if (root && root.__isShadowRoot) { scope = root; inShadow = true; }
|
|
248
|
-
}
|
|
249
|
-
|
|
250
279
|
const matched = [];
|
|
251
|
-
collectMatched(el, getIndex(
|
|
252
|
-
if (doc.__hasShadow)
|
|
253
|
-
// shadow host: overlay :host rules from its OWN shadow root's stylesheet
|
|
254
|
-
if (el.__shadow) collectMatched(el, getIndex(el.__shadow, v), 'host', matched);
|
|
255
|
-
// slotted light node: overlay ::slotted rules from the slot's shadow root
|
|
256
|
-
const slot = el.assignedSlot;
|
|
257
|
-
if (slot) { const sr = slot.getRootNode(); if (sr.__isShadowRoot) collectMatched(el, getIndex(sr, v), 'slotted', matched, slot); }
|
|
258
|
-
}
|
|
280
|
+
collectMatched(el, getIndex(styleScope(doc, el), v), 'normal', matched);
|
|
281
|
+
if (doc.__hasShadow) collectShadowMatched(el, v, matched);
|
|
259
282
|
applyMatched(matched, map);
|
|
260
283
|
|
|
261
284
|
// inline style wins over stylesheet rules
|
|
@@ -268,17 +291,10 @@ export function makeGetComputedStyle() {
|
|
|
268
291
|
// shadow content. Honest partial: only the curated INHERITED set inherits, only
|
|
269
292
|
// from values a REAL rule/inline actually set (pv truthy) — never an initial
|
|
270
293
|
// value. Recursion is memoized per element/version and terminates at the root
|
|
271
|
-
// (flattenedParent → null). Test-time only; hot paths never call gcs.
|
|
272
|
-
//
|
|
273
|
-
// hop still crosses the host boundary correctly for shadow content.
|
|
274
|
-
void inShadow;
|
|
294
|
+
// (flattenedParent → null). Test-time only; hot paths never call gcs. The
|
|
295
|
+
// flattened-parent hop crosses the host boundary for shadow content.
|
|
275
296
|
const parent = flattenedParent(el);
|
|
276
|
-
if (parent)
|
|
277
|
-
const ps = gcs(parent);
|
|
278
|
-
for (const prop of INHERITED) {
|
|
279
|
-
if (!map.has(prop)) { const pv = ps.getPropertyValue(prop); if (pv) map.set(prop, pv); }
|
|
280
|
-
}
|
|
281
|
-
}
|
|
297
|
+
if (parent) inheritFrom(map, gcs(parent));
|
|
282
298
|
|
|
283
299
|
const proxy = makeProxy(map, v);
|
|
284
300
|
el.__computedStyle = proxy;
|