buninu-browser 0.5.0 → 0.6.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/README.md +9 -3
- package/lib/audio/pcm.js +11 -0
- package/lib/audio/pulse-client.js +7 -2
- package/lib/cdp-server.js +31 -7
- package/lib/happy-dom/document-base.js +29 -1
- package/lib/happy-dom/inert-link.js +26 -0
- package/lib/happy-dom/mutation-observer.js +157 -0
- package/lib/happy-dom/node-iterator-removal.js +36 -0
- package/lib/happy-dom/node-replacement.js +60 -0
- package/lib/happy-dom/parser.js +6 -1
- package/lib/headless-shell.js +49 -9
- package/lib/layout/repaint.js +89 -0
- package/lib/layout/text-layout.js +4 -2
- package/lib/media/mp4-keyframes.js +144 -0
- package/lib/media/video-keyframe.js +29 -1
- package/lib/media/webm-snapshots.js +282 -0
- package/lib/network/cookie-jar.js +18 -2
- package/lib/paint/display-list.js +4 -3
- package/lib/paint/frame.js +12 -5
- package/lib/render-tree/index.js +3 -0
- package/lib/renderer/animation-frames.js +76 -0
- package/lib/renderer/bindings.js +107 -3
- package/lib/renderer/compositor-channel.js +95 -10
- package/lib/renderer/compositor.js +54 -18
- package/lib/renderer/document-cookies.js +87 -0
- package/lib/renderer/font-loading.js +60 -16
- package/lib/renderer/host.js +48 -19
- package/lib/renderer/idle-callbacks.js +109 -0
- package/lib/renderer/main.js +5 -1
- package/lib/renderer/media-source.js +4 -0
- package/lib/renderer/page-realm.js +159 -49
- package/lib/renderer/page-renderer.js +577 -201
- package/lib/renderer/resource-elements.js +88 -0
- package/lib/renderer/script-rewrite.js +18 -1
- package/lib/renderer/seccomp.js +6 -4
- package/lib/renderer/timers.js +71 -0
- package/lib/renderer/video-channel.js +94 -0
- package/lib/renderer/video-mask.js +115 -0
- package/lib/renderer/video-presentation.js +304 -39
- package/lib/renderer/video-worker.js +46 -0
- package/lib/renderer/workers.js +7 -17
- package/lib/style/computed-style.js +257 -61
- package/package.json +1 -1
- package/test/animation-frames.test.js +67 -0
- package/test/canvas-style-snapshot.test.js +18 -0
- package/test/cdp-stream.test.js +96 -3
- package/test/compare-node-iterator.js +42 -0
- package/test/compositor-channel-ownership.test.js +194 -0
- package/test/compositor.test.js +27 -1
- package/test/computed-style-binding.test.js +49 -0
- package/test/computed-style-connectivity.test.js +13 -0
- package/test/computed-style.test.js +86 -0
- package/test/cookie-sync.test.js +82 -0
- package/test/document-base-cache.test.js +103 -0
- package/test/document-cookies.test.js +117 -0
- package/test/duplicate-style-keys.test.js +11 -0
- package/test/fixtures/animation-frames.js +56 -0
- package/test/fixtures/canvas-style-snapshot.js +81 -0
- package/test/fixtures/computed-style-connectivity.js +101 -0
- package/test/fixtures/computed-style-lazy.js +135 -0
- package/test/fixtures/duplicate-style-keys.js +97 -0
- package/test/fixtures/fake-renderer.js +12 -0
- package/test/fixtures/font-definition-invalidation.js +97 -0
- package/test/fixtures/font-metrics-invalidation.js +87 -0
- package/test/fixtures/fractional-text-scale.js +38 -0
- package/test/fixtures/full-style-identity.js +36 -0
- package/test/fixtures/hidden-style-engine.js +260 -0
- package/test/fixtures/hidden-style-reentry.js +232 -0
- package/test/fixtures/hidden-style-renderer.js +203 -0
- package/test/fixtures/layout-repaint.js +69 -0
- package/test/fixtures/media-presentation-continuation.js +141 -0
- package/test/fixtures/media-presentation-refill.js +113 -0
- package/test/fixtures/media-probe.js +148 -0
- package/test/fixtures/media-refill-cancel.js +79 -0
- package/test/fixtures/media-repaint.js +48 -0
- package/test/fixtures/media-resource-reselection.js +94 -0
- package/test/fixtures/media-source-attachment.js +136 -0
- package/test/fixtures/media-source-no-decoder.js +17 -0
- package/test/fixtures/media-track-refill.js +132 -0
- package/test/fixtures/media-webm-seek.js +51 -0
- package/test/fixtures/mutation-observer.js +201 -0
- package/test/fixtures/node-iterator-cases.js +59 -0
- package/test/fixtures/node-iterator-chromium.json +315 -0
- package/test/fixtures/observer-invalidation.js +105 -0
- package/test/fixtures/prepaint-scheduling.js +73 -0
- package/test/fixtures/resource-discovery.js +32 -0
- package/test/fixtures/spare-renderer-scheduling.js +124 -0
- package/test/fixtures/style-propagation.js +26 -0
- package/test/fixtures/svg-node-reuse.js +83 -0
- package/test/fixtures/video-black-replay.js +140 -0
- package/test/fixtures/video-controls-invalidation.js +71 -0
- package/test/fixtures/viewport-bounds.js +148 -0
- package/test/font-loading.test.js +77 -0
- package/test/font-metrics-invalidation.test.js +60 -0
- package/test/fractional-text-scale.test.js +6 -0
- package/test/full-style-identity.test.js +191 -0
- package/test/hidden-style-lazy.test.js +31 -0
- package/test/hidden-style-reentry.test.js +20 -0
- package/test/idle-callback.test.js +89 -0
- package/test/idle-scheduler.test.js +148 -0
- package/test/inert-link.test.js +37 -0
- package/test/inline-style-cascade.test.js +63 -0
- package/test/layout-repaint-renderer.test.js +9 -0
- package/test/layout-repaint.test.js +222 -0
- package/test/media-presentation-continuation.test.js +14 -0
- package/test/media-presentation-refill.test.js +8 -0
- package/test/media-probe.test.js +16 -0
- package/test/media-repaint.test.js +8 -0
- package/test/media-source-attachment.test.js +34 -0
- package/test/media-track-refill.test.js +26 -0
- package/test/media-webm-seek.test.js +11 -0
- package/test/mp4-keyframes.test.js +36 -0
- package/test/mutation-observer.test.js +91 -0
- package/test/node-iterator.test.js +81 -0
- package/test/observer-invalidation.test.js +12 -0
- package/test/page-scripts.test.js +151 -1
- package/test/page-timers.test.js +67 -0
- package/test/paint-frame.test.js +18 -1
- package/test/pcm.test.js +63 -0
- package/test/prepaint-scheduling.test.js +13 -0
- package/test/pulse-client.test.js +165 -0
- package/test/raw-capture.test.js +34 -5
- package/test/renderer-host.test.js +51 -0
- package/test/resource-discovery.test.js +12 -0
- package/test/resource-elements.test.js +234 -0
- package/test/root-self-style.test.js +104 -0
- package/test/script-rewrite.test.js +35 -0
- package/test/seccomp.test.js +45 -0
- package/test/spare-renderer-scheduling.test.js +8 -0
- package/test/style-propagation.test.js +225 -0
- package/test/style-sheets.test.js +19 -0
- package/test/svg.test.js +45 -0
- package/test/text-layout.test.js +15 -0
- package/test/timers.test.js +58 -0
- package/test/video-black-replay.test.js +34 -0
- package/test/video-capture.test.js +170 -0
- package/test/video-channel.test.js +253 -0
- package/test/video-continuation.test.js +57 -0
- package/test/video-controls-invalidation.test.js +12 -0
- package/test/video-keyframe.test.js +104 -4
- package/test/video-mask.test.js +94 -0
- package/test/video-presentation.test.js +239 -1
- package/test/video-timing-presentation.test.js +115 -0
- package/test/viewport-bounds.test.js +13 -0
- package/test/webm-snapshots.test.js +372 -0
- package/test/window-resize.test.js +29 -0
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
// Runs unchanged in the renderer and Chromium; keep this function standalone.
|
|
2
|
+
export function nodeIteratorCases() {
|
|
3
|
+
const results = [];
|
|
4
|
+
const label = n => n ? n.id || n.nodeName : null;
|
|
5
|
+
for (const tag of ['div', 'form', 'select'])
|
|
6
|
+
for (const operation of ['remove', 'replace', 'replaceWith', 'self', 'sibling', 'fragment', 'descendant', 'multiple'])
|
|
7
|
+
for (const position of [0, 1, 2, 3]) for (const before of [false, true]) {
|
|
8
|
+
const root = document.createElement(tag);root.id = 'root';
|
|
9
|
+
for (const id of ['a', 'b']) {
|
|
10
|
+
const node = document.createElement('option');node.id = id;root.append(node);
|
|
11
|
+
}
|
|
12
|
+
const nested = document.createElement('span');nested.id = 'nested';root.firstChild.append(nested);
|
|
13
|
+
const it = document.createNodeIterator(root, 1);
|
|
14
|
+
for (let i = 0; i <= position; i++) it.nextNode();
|
|
15
|
+
if (before) it.previousNode();
|
|
16
|
+
const old = root.firstChild, x = document.createElement('em');x.id = 'x';
|
|
17
|
+
if (operation === 'remove') old.remove();
|
|
18
|
+
if (operation === 'replace') root.replaceChild(x, old);
|
|
19
|
+
if (operation === 'replaceWith') old.replaceWith(x);
|
|
20
|
+
if (operation === 'self') root.replaceChild(old, old);
|
|
21
|
+
if (operation === 'sibling') root.replaceChild(root.lastChild, old);
|
|
22
|
+
if (operation === 'descendant') root.replaceChild(nested, old);
|
|
23
|
+
if (operation === 'fragment') {
|
|
24
|
+
const fragment = document.createDocumentFragment();fragment.append(x);
|
|
25
|
+
const y = document.createElement('em');y.id = 'y';fragment.append(y);
|
|
26
|
+
root.replaceChild(fragment, old);
|
|
27
|
+
}
|
|
28
|
+
if (operation === 'multiple') old.replaceWith(x, old, root.lastChild);
|
|
29
|
+
results.push([tag, operation, position, before, label(it.referenceNode),
|
|
30
|
+
it.pointerBeforeReferenceNode, label(it.nextNode()), label(it.previousNode()),
|
|
31
|
+
[...root.childNodes].map(label).join(',')]);
|
|
32
|
+
}
|
|
33
|
+
for (const whatToShow of [0, 1, -1, 4294967297, NaN, Infinity, '1', null, 1n, Object(1n), Symbol('x')]) {
|
|
34
|
+
try { results.push(['mask', String(whatToShow), document.createNodeIterator(document.body, whatToShow).whatToShow]); }
|
|
35
|
+
catch (e) { results.push(['mask', String(whatToShow), e.name]); }
|
|
36
|
+
}
|
|
37
|
+
for (const verdict of [1, 65537, -65535, NaN, Infinity, '1', null, 1n, Object(1n), Symbol('x')]) {
|
|
38
|
+
try { results.push(['filter', String(verdict), !!document.createNodeIterator(document.body, 1, () => verdict).nextNode()]); }
|
|
39
|
+
catch (e) { results.push(['filter', String(verdict), e.name]); }
|
|
40
|
+
}
|
|
41
|
+
// Filtering and root types: rejection must not prune descendants.
|
|
42
|
+
for (const rootKind of ['element', 'fragment', 'text', 'comment', 'doctype'])
|
|
43
|
+
for (const mask of [0, 1, 4, 128, 0xffffffff]) for (const mode of ['accept', 'reject', 'skip', 'alternating']) {
|
|
44
|
+
const root = rootKind === 'fragment' ? document.createDocumentFragment()
|
|
45
|
+
: rootKind === 'text' ? document.createTextNode('text')
|
|
46
|
+
: rootKind === 'comment' ? document.createComment('comment')
|
|
47
|
+
: rootKind === 'doctype' ? document.implementation.createDocumentType('html', '', '') : document.createElement('div');
|
|
48
|
+
if (rootKind === 'element' || rootKind === 'fragment') {
|
|
49
|
+
const child = document.createElement('b');child.append('text');root.append(child, document.createComment('comment'));
|
|
50
|
+
}
|
|
51
|
+
const it = document.createNodeIterator(root, mask, n => mode === 'accept' ? 1
|
|
52
|
+
: mode === 'reject' ? 2 : mode === 'skip' ? 3 : n.nodeType === 1 ? 2 : 1);
|
|
53
|
+
const forward = [], backward = [];
|
|
54
|
+
for (let n; (n = it.nextNode());) forward.push(n.nodeType);
|
|
55
|
+
for (let n; (n = it.previousNode());) backward.push(n.nodeType);
|
|
56
|
+
results.push(['walk', rootKind, mask, mode, forward.join(','), backward.join(',')]);
|
|
57
|
+
}
|
|
58
|
+
return results;
|
|
59
|
+
}
|
|
@@ -0,0 +1,315 @@
|
|
|
1
|
+
[
|
|
2
|
+
["div","remove",0,false,"root",false,"b","b","b"],
|
|
3
|
+
["div","remove",0,true,"root",true,"root","root","b"],
|
|
4
|
+
["div","remove",1,false,"root",false,"b","b","b"],
|
|
5
|
+
["div","remove",1,true,"b",true,"b","b","b"],
|
|
6
|
+
["div","remove",2,false,"root",false,"b","b","b"],
|
|
7
|
+
["div","remove",2,true,"b",true,"b","b","b"],
|
|
8
|
+
["div","remove",3,false,"b",false,null,"b","b"],
|
|
9
|
+
["div","remove",3,true,"b",true,"b","b","b"],
|
|
10
|
+
["div","replace",0,false,"root",false,"x","x","x,b"],
|
|
11
|
+
["div","replace",0,true,"root",true,"root","root","x,b"],
|
|
12
|
+
["div","replace",1,false,"root",false,"x","x","x,b"],
|
|
13
|
+
["div","replace",1,true,"b",true,"b","b","x,b"],
|
|
14
|
+
["div","replace",2,false,"root",false,"x","x","x,b"],
|
|
15
|
+
["div","replace",2,true,"b",true,"b","b","x,b"],
|
|
16
|
+
["div","replace",3,false,"b",false,null,"b","x,b"],
|
|
17
|
+
["div","replace",3,true,"b",true,"b","b","x,b"],
|
|
18
|
+
["div","replaceWith",0,false,"root",false,"x","x","x,b"],
|
|
19
|
+
["div","replaceWith",0,true,"root",true,"root","root","x,b"],
|
|
20
|
+
["div","replaceWith",1,false,"root",false,"x","x","x,b"],
|
|
21
|
+
["div","replaceWith",1,true,"b",true,"b","b","x,b"],
|
|
22
|
+
["div","replaceWith",2,false,"root",false,"x","x","x,b"],
|
|
23
|
+
["div","replaceWith",2,true,"b",true,"b","b","x,b"],
|
|
24
|
+
["div","replaceWith",3,false,"b",false,null,"b","x,b"],
|
|
25
|
+
["div","replaceWith",3,true,"b",true,"b","b","x,b"],
|
|
26
|
+
["div","self",0,false,"root",false,"a","a","a,b"],
|
|
27
|
+
["div","self",0,true,"root",true,"root","root","a,b"],
|
|
28
|
+
["div","self",1,false,"root",false,"a","a","a,b"],
|
|
29
|
+
["div","self",1,true,"b",true,"b","b","a,b"],
|
|
30
|
+
["div","self",2,false,"root",false,"a","a","a,b"],
|
|
31
|
+
["div","self",2,true,"b",true,"b","b","a,b"],
|
|
32
|
+
["div","self",3,false,"b",false,null,"b","a,b"],
|
|
33
|
+
["div","self",3,true,"b",true,"b","b","a,b"],
|
|
34
|
+
["div","sibling",0,false,"root",false,"b","b","b"],
|
|
35
|
+
["div","sibling",0,true,"root",true,"root","root","b"],
|
|
36
|
+
["div","sibling",1,false,"root",false,"b","b","b"],
|
|
37
|
+
["div","sibling",1,true,"a",true,"a","a","b"],
|
|
38
|
+
["div","sibling",2,false,"root",false,"b","b","b"],
|
|
39
|
+
["div","sibling",2,true,"nested",true,"nested","nested","b"],
|
|
40
|
+
["div","sibling",3,false,"root",false,"b","b","b"],
|
|
41
|
+
["div","sibling",3,true,"root",false,"b","b","b"],
|
|
42
|
+
["div","fragment",0,false,"root",false,"x","x","x,y,b"],
|
|
43
|
+
["div","fragment",0,true,"root",true,"root","root","x,y,b"],
|
|
44
|
+
["div","fragment",1,false,"root",false,"x","x","x,y,b"],
|
|
45
|
+
["div","fragment",1,true,"b",true,"b","b","x,y,b"],
|
|
46
|
+
["div","fragment",2,false,"root",false,"x","x","x,y,b"],
|
|
47
|
+
["div","fragment",2,true,"b",true,"b","b","x,y,b"],
|
|
48
|
+
["div","fragment",3,false,"b",false,null,"b","x,y,b"],
|
|
49
|
+
["div","fragment",3,true,"b",true,"b","b","x,y,b"],
|
|
50
|
+
["div","descendant",0,false,"root",false,"nested","nested","nested,b"],
|
|
51
|
+
["div","descendant",0,true,"root",true,"root","root","nested,b"],
|
|
52
|
+
["div","descendant",1,false,"root",false,"nested","nested","nested,b"],
|
|
53
|
+
["div","descendant",1,true,"b",true,"b","b","nested,b"],
|
|
54
|
+
["div","descendant",2,false,"root",false,"nested","nested","nested,b"],
|
|
55
|
+
["div","descendant",2,true,"b",true,"b","b","nested,b"],
|
|
56
|
+
["div","descendant",3,false,"b",false,null,"b","nested,b"],
|
|
57
|
+
["div","descendant",3,true,"b",true,"b","b","nested,b"],
|
|
58
|
+
["div","multiple",0,false,"root",false,"x","x","x,a,b"],
|
|
59
|
+
["div","multiple",0,true,"root",true,"root","root","x,a,b"],
|
|
60
|
+
["div","multiple",1,false,"root",false,"x","x","x,a,b"],
|
|
61
|
+
["div","multiple",1,true,"root",false,"x","x","x,a,b"],
|
|
62
|
+
["div","multiple",2,false,"root",false,"x","x","x,a,b"],
|
|
63
|
+
["div","multiple",2,true,"root",false,"x","x","x,a,b"],
|
|
64
|
+
["div","multiple",3,false,"root",false,"x","x","x,a,b"],
|
|
65
|
+
["div","multiple",3,true,"root",false,"x","x","x,a,b"],
|
|
66
|
+
["form","remove",0,false,"root",false,"b","b","b"],
|
|
67
|
+
["form","remove",0,true,"root",true,"root","root","b"],
|
|
68
|
+
["form","remove",1,false,"root",false,"b","b","b"],
|
|
69
|
+
["form","remove",1,true,"b",true,"b","b","b"],
|
|
70
|
+
["form","remove",2,false,"root",false,"b","b","b"],
|
|
71
|
+
["form","remove",2,true,"b",true,"b","b","b"],
|
|
72
|
+
["form","remove",3,false,"b",false,null,"b","b"],
|
|
73
|
+
["form","remove",3,true,"b",true,"b","b","b"],
|
|
74
|
+
["form","replace",0,false,"root",false,"x","x","x,b"],
|
|
75
|
+
["form","replace",0,true,"root",true,"root","root","x,b"],
|
|
76
|
+
["form","replace",1,false,"root",false,"x","x","x,b"],
|
|
77
|
+
["form","replace",1,true,"b",true,"b","b","x,b"],
|
|
78
|
+
["form","replace",2,false,"root",false,"x","x","x,b"],
|
|
79
|
+
["form","replace",2,true,"b",true,"b","b","x,b"],
|
|
80
|
+
["form","replace",3,false,"b",false,null,"b","x,b"],
|
|
81
|
+
["form","replace",3,true,"b",true,"b","b","x,b"],
|
|
82
|
+
["form","replaceWith",0,false,"root",false,"x","x","x,b"],
|
|
83
|
+
["form","replaceWith",0,true,"root",true,"root","root","x,b"],
|
|
84
|
+
["form","replaceWith",1,false,"root",false,"x","x","x,b"],
|
|
85
|
+
["form","replaceWith",1,true,"b",true,"b","b","x,b"],
|
|
86
|
+
["form","replaceWith",2,false,"root",false,"x","x","x,b"],
|
|
87
|
+
["form","replaceWith",2,true,"b",true,"b","b","x,b"],
|
|
88
|
+
["form","replaceWith",3,false,"b",false,null,"b","x,b"],
|
|
89
|
+
["form","replaceWith",3,true,"b",true,"b","b","x,b"],
|
|
90
|
+
["form","self",0,false,"root",false,"a","a","a,b"],
|
|
91
|
+
["form","self",0,true,"root",true,"root","root","a,b"],
|
|
92
|
+
["form","self",1,false,"root",false,"a","a","a,b"],
|
|
93
|
+
["form","self",1,true,"b",true,"b","b","a,b"],
|
|
94
|
+
["form","self",2,false,"root",false,"a","a","a,b"],
|
|
95
|
+
["form","self",2,true,"b",true,"b","b","a,b"],
|
|
96
|
+
["form","self",3,false,"b",false,null,"b","a,b"],
|
|
97
|
+
["form","self",3,true,"b",true,"b","b","a,b"],
|
|
98
|
+
["form","sibling",0,false,"root",false,"b","b","b"],
|
|
99
|
+
["form","sibling",0,true,"root",true,"root","root","b"],
|
|
100
|
+
["form","sibling",1,false,"root",false,"b","b","b"],
|
|
101
|
+
["form","sibling",1,true,"a",true,"a","a","b"],
|
|
102
|
+
["form","sibling",2,false,"root",false,"b","b","b"],
|
|
103
|
+
["form","sibling",2,true,"nested",true,"nested","nested","b"],
|
|
104
|
+
["form","sibling",3,false,"root",false,"b","b","b"],
|
|
105
|
+
["form","sibling",3,true,"root",false,"b","b","b"],
|
|
106
|
+
["form","fragment",0,false,"root",false,"x","x","x,y,b"],
|
|
107
|
+
["form","fragment",0,true,"root",true,"root","root","x,y,b"],
|
|
108
|
+
["form","fragment",1,false,"root",false,"x","x","x,y,b"],
|
|
109
|
+
["form","fragment",1,true,"b",true,"b","b","x,y,b"],
|
|
110
|
+
["form","fragment",2,false,"root",false,"x","x","x,y,b"],
|
|
111
|
+
["form","fragment",2,true,"b",true,"b","b","x,y,b"],
|
|
112
|
+
["form","fragment",3,false,"b",false,null,"b","x,y,b"],
|
|
113
|
+
["form","fragment",3,true,"b",true,"b","b","x,y,b"],
|
|
114
|
+
["form","descendant",0,false,"root",false,"nested","nested","nested,b"],
|
|
115
|
+
["form","descendant",0,true,"root",true,"root","root","nested,b"],
|
|
116
|
+
["form","descendant",1,false,"root",false,"nested","nested","nested,b"],
|
|
117
|
+
["form","descendant",1,true,"b",true,"b","b","nested,b"],
|
|
118
|
+
["form","descendant",2,false,"root",false,"nested","nested","nested,b"],
|
|
119
|
+
["form","descendant",2,true,"b",true,"b","b","nested,b"],
|
|
120
|
+
["form","descendant",3,false,"b",false,null,"b","nested,b"],
|
|
121
|
+
["form","descendant",3,true,"b",true,"b","b","nested,b"],
|
|
122
|
+
["form","multiple",0,false,"root",false,"x","x","x,a,b"],
|
|
123
|
+
["form","multiple",0,true,"root",true,"root","root","x,a,b"],
|
|
124
|
+
["form","multiple",1,false,"root",false,"x","x","x,a,b"],
|
|
125
|
+
["form","multiple",1,true,"root",false,"x","x","x,a,b"],
|
|
126
|
+
["form","multiple",2,false,"root",false,"x","x","x,a,b"],
|
|
127
|
+
["form","multiple",2,true,"root",false,"x","x","x,a,b"],
|
|
128
|
+
["form","multiple",3,false,"root",false,"x","x","x,a,b"],
|
|
129
|
+
["form","multiple",3,true,"root",false,"x","x","x,a,b"],
|
|
130
|
+
["select","remove",0,false,"root",false,"b","b","b"],
|
|
131
|
+
["select","remove",0,true,"root",true,"root","root","b"],
|
|
132
|
+
["select","remove",1,false,"root",false,"b","b","b"],
|
|
133
|
+
["select","remove",1,true,"b",true,"b","b","b"],
|
|
134
|
+
["select","remove",2,false,"root",false,"b","b","b"],
|
|
135
|
+
["select","remove",2,true,"b",true,"b","b","b"],
|
|
136
|
+
["select","remove",3,false,"b",false,null,"b","b"],
|
|
137
|
+
["select","remove",3,true,"b",true,"b","b","b"],
|
|
138
|
+
["select","replace",0,false,"root",false,"x","x","x,b"],
|
|
139
|
+
["select","replace",0,true,"root",true,"root","root","x,b"],
|
|
140
|
+
["select","replace",1,false,"root",false,"x","x","x,b"],
|
|
141
|
+
["select","replace",1,true,"b",true,"b","b","x,b"],
|
|
142
|
+
["select","replace",2,false,"root",false,"x","x","x,b"],
|
|
143
|
+
["select","replace",2,true,"b",true,"b","b","x,b"],
|
|
144
|
+
["select","replace",3,false,"b",false,null,"b","x,b"],
|
|
145
|
+
["select","replace",3,true,"b",true,"b","b","x,b"],
|
|
146
|
+
["select","replaceWith",0,false,"root",false,"x","x","x,b"],
|
|
147
|
+
["select","replaceWith",0,true,"root",true,"root","root","x,b"],
|
|
148
|
+
["select","replaceWith",1,false,"root",false,"x","x","x,b"],
|
|
149
|
+
["select","replaceWith",1,true,"b",true,"b","b","x,b"],
|
|
150
|
+
["select","replaceWith",2,false,"root",false,"x","x","x,b"],
|
|
151
|
+
["select","replaceWith",2,true,"b",true,"b","b","x,b"],
|
|
152
|
+
["select","replaceWith",3,false,"b",false,null,"b","x,b"],
|
|
153
|
+
["select","replaceWith",3,true,"b",true,"b","b","x,b"],
|
|
154
|
+
["select","self",0,false,"root",false,"a","a","a,b"],
|
|
155
|
+
["select","self",0,true,"root",true,"root","root","a,b"],
|
|
156
|
+
["select","self",1,false,"root",false,"a","a","a,b"],
|
|
157
|
+
["select","self",1,true,"b",true,"b","b","a,b"],
|
|
158
|
+
["select","self",2,false,"root",false,"a","a","a,b"],
|
|
159
|
+
["select","self",2,true,"b",true,"b","b","a,b"],
|
|
160
|
+
["select","self",3,false,"b",false,null,"b","a,b"],
|
|
161
|
+
["select","self",3,true,"b",true,"b","b","a,b"],
|
|
162
|
+
["select","sibling",0,false,"root",false,"b","b","b"],
|
|
163
|
+
["select","sibling",0,true,"root",true,"root","root","b"],
|
|
164
|
+
["select","sibling",1,false,"root",false,"b","b","b"],
|
|
165
|
+
["select","sibling",1,true,"a",true,"a","a","b"],
|
|
166
|
+
["select","sibling",2,false,"root",false,"b","b","b"],
|
|
167
|
+
["select","sibling",2,true,"nested",true,"nested","nested","b"],
|
|
168
|
+
["select","sibling",3,false,"root",false,"b","b","b"],
|
|
169
|
+
["select","sibling",3,true,"root",false,"b","b","b"],
|
|
170
|
+
["select","fragment",0,false,"root",false,"x","x","x,y,b"],
|
|
171
|
+
["select","fragment",0,true,"root",true,"root","root","x,y,b"],
|
|
172
|
+
["select","fragment",1,false,"root",false,"x","x","x,y,b"],
|
|
173
|
+
["select","fragment",1,true,"b",true,"b","b","x,y,b"],
|
|
174
|
+
["select","fragment",2,false,"root",false,"x","x","x,y,b"],
|
|
175
|
+
["select","fragment",2,true,"b",true,"b","b","x,y,b"],
|
|
176
|
+
["select","fragment",3,false,"b",false,null,"b","x,y,b"],
|
|
177
|
+
["select","fragment",3,true,"b",true,"b","b","x,y,b"],
|
|
178
|
+
["select","descendant",0,false,"root",false,"nested","nested","nested,b"],
|
|
179
|
+
["select","descendant",0,true,"root",true,"root","root","nested,b"],
|
|
180
|
+
["select","descendant",1,false,"root",false,"nested","nested","nested,b"],
|
|
181
|
+
["select","descendant",1,true,"b",true,"b","b","nested,b"],
|
|
182
|
+
["select","descendant",2,false,"root",false,"nested","nested","nested,b"],
|
|
183
|
+
["select","descendant",2,true,"b",true,"b","b","nested,b"],
|
|
184
|
+
["select","descendant",3,false,"b",false,null,"b","nested,b"],
|
|
185
|
+
["select","descendant",3,true,"b",true,"b","b","nested,b"],
|
|
186
|
+
["select","multiple",0,false,"root",false,"x","x","x,a,b"],
|
|
187
|
+
["select","multiple",0,true,"root",true,"root","root","x,a,b"],
|
|
188
|
+
["select","multiple",1,false,"root",false,"x","x","x,a,b"],
|
|
189
|
+
["select","multiple",1,true,"root",false,"x","x","x,a,b"],
|
|
190
|
+
["select","multiple",2,false,"root",false,"x","x","x,a,b"],
|
|
191
|
+
["select","multiple",2,true,"root",false,"x","x","x,a,b"],
|
|
192
|
+
["select","multiple",3,false,"root",false,"x","x","x,a,b"],
|
|
193
|
+
["select","multiple",3,true,"root",false,"x","x","x,a,b"],
|
|
194
|
+
["mask","0",0],
|
|
195
|
+
["mask","1",1],
|
|
196
|
+
["mask","-1",4294967295],
|
|
197
|
+
["mask","4294967297",1],
|
|
198
|
+
["mask","NaN",0],
|
|
199
|
+
["mask","Infinity",0],
|
|
200
|
+
["mask","1",1],
|
|
201
|
+
["mask","null",0],
|
|
202
|
+
["mask","1","TypeError"],
|
|
203
|
+
["mask","1","TypeError"],
|
|
204
|
+
["mask","Symbol(x)","TypeError"],
|
|
205
|
+
["filter","1",true],
|
|
206
|
+
["filter","65537",true],
|
|
207
|
+
["filter","-65535",true],
|
|
208
|
+
["filter","NaN",false],
|
|
209
|
+
["filter","Infinity",false],
|
|
210
|
+
["filter","1",true],
|
|
211
|
+
["filter","null",false],
|
|
212
|
+
["filter","1","TypeError"],
|
|
213
|
+
["filter","1","TypeError"],
|
|
214
|
+
["filter","Symbol(x)","TypeError"],
|
|
215
|
+
["walk","element",0,"accept","",""],
|
|
216
|
+
["walk","element",0,"reject","",""],
|
|
217
|
+
["walk","element",0,"skip","",""],
|
|
218
|
+
["walk","element",0,"alternating","",""],
|
|
219
|
+
["walk","element",1,"accept","1,1","1,1"],
|
|
220
|
+
["walk","element",1,"reject","",""],
|
|
221
|
+
["walk","element",1,"skip","",""],
|
|
222
|
+
["walk","element",1,"alternating","",""],
|
|
223
|
+
["walk","element",4,"accept","3","3"],
|
|
224
|
+
["walk","element",4,"reject","",""],
|
|
225
|
+
["walk","element",4,"skip","",""],
|
|
226
|
+
["walk","element",4,"alternating","3","3"],
|
|
227
|
+
["walk","element",128,"accept","8","8"],
|
|
228
|
+
["walk","element",128,"reject","",""],
|
|
229
|
+
["walk","element",128,"skip","",""],
|
|
230
|
+
["walk","element",128,"alternating","8","8"],
|
|
231
|
+
["walk","element",4294967295,"accept","1,1,3,8","8,3,1,1"],
|
|
232
|
+
["walk","element",4294967295,"reject","",""],
|
|
233
|
+
["walk","element",4294967295,"skip","",""],
|
|
234
|
+
["walk","element",4294967295,"alternating","3,8","8,3"],
|
|
235
|
+
["walk","fragment",0,"accept","",""],
|
|
236
|
+
["walk","fragment",0,"reject","",""],
|
|
237
|
+
["walk","fragment",0,"skip","",""],
|
|
238
|
+
["walk","fragment",0,"alternating","",""],
|
|
239
|
+
["walk","fragment",1,"accept","1","1"],
|
|
240
|
+
["walk","fragment",1,"reject","",""],
|
|
241
|
+
["walk","fragment",1,"skip","",""],
|
|
242
|
+
["walk","fragment",1,"alternating","",""],
|
|
243
|
+
["walk","fragment",4,"accept","3","3"],
|
|
244
|
+
["walk","fragment",4,"reject","",""],
|
|
245
|
+
["walk","fragment",4,"skip","",""],
|
|
246
|
+
["walk","fragment",4,"alternating","3","3"],
|
|
247
|
+
["walk","fragment",128,"accept","8","8"],
|
|
248
|
+
["walk","fragment",128,"reject","",""],
|
|
249
|
+
["walk","fragment",128,"skip","",""],
|
|
250
|
+
["walk","fragment",128,"alternating","8","8"],
|
|
251
|
+
["walk","fragment",4294967295,"accept","11,1,3,8","8,3,1,11"],
|
|
252
|
+
["walk","fragment",4294967295,"reject","",""],
|
|
253
|
+
["walk","fragment",4294967295,"skip","",""],
|
|
254
|
+
["walk","fragment",4294967295,"alternating","11,3,8","8,3,11"],
|
|
255
|
+
["walk","text",0,"accept","",""],
|
|
256
|
+
["walk","text",0,"reject","",""],
|
|
257
|
+
["walk","text",0,"skip","",""],
|
|
258
|
+
["walk","text",0,"alternating","",""],
|
|
259
|
+
["walk","text",1,"accept","",""],
|
|
260
|
+
["walk","text",1,"reject","",""],
|
|
261
|
+
["walk","text",1,"skip","",""],
|
|
262
|
+
["walk","text",1,"alternating","",""],
|
|
263
|
+
["walk","text",4,"accept","3","3"],
|
|
264
|
+
["walk","text",4,"reject","",""],
|
|
265
|
+
["walk","text",4,"skip","",""],
|
|
266
|
+
["walk","text",4,"alternating","3","3"],
|
|
267
|
+
["walk","text",128,"accept","",""],
|
|
268
|
+
["walk","text",128,"reject","",""],
|
|
269
|
+
["walk","text",128,"skip","",""],
|
|
270
|
+
["walk","text",128,"alternating","",""],
|
|
271
|
+
["walk","text",4294967295,"accept","3","3"],
|
|
272
|
+
["walk","text",4294967295,"reject","",""],
|
|
273
|
+
["walk","text",4294967295,"skip","",""],
|
|
274
|
+
["walk","text",4294967295,"alternating","3","3"],
|
|
275
|
+
["walk","comment",0,"accept","",""],
|
|
276
|
+
["walk","comment",0,"reject","",""],
|
|
277
|
+
["walk","comment",0,"skip","",""],
|
|
278
|
+
["walk","comment",0,"alternating","",""],
|
|
279
|
+
["walk","comment",1,"accept","",""],
|
|
280
|
+
["walk","comment",1,"reject","",""],
|
|
281
|
+
["walk","comment",1,"skip","",""],
|
|
282
|
+
["walk","comment",1,"alternating","",""],
|
|
283
|
+
["walk","comment",4,"accept","",""],
|
|
284
|
+
["walk","comment",4,"reject","",""],
|
|
285
|
+
["walk","comment",4,"skip","",""],
|
|
286
|
+
["walk","comment",4,"alternating","",""],
|
|
287
|
+
["walk","comment",128,"accept","8","8"],
|
|
288
|
+
["walk","comment",128,"reject","",""],
|
|
289
|
+
["walk","comment",128,"skip","",""],
|
|
290
|
+
["walk","comment",128,"alternating","8","8"],
|
|
291
|
+
["walk","comment",4294967295,"accept","8","8"],
|
|
292
|
+
["walk","comment",4294967295,"reject","",""],
|
|
293
|
+
["walk","comment",4294967295,"skip","",""],
|
|
294
|
+
["walk","comment",4294967295,"alternating","8","8"],
|
|
295
|
+
["walk","doctype",0,"accept","",""],
|
|
296
|
+
["walk","doctype",0,"reject","",""],
|
|
297
|
+
["walk","doctype",0,"skip","",""],
|
|
298
|
+
["walk","doctype",0,"alternating","",""],
|
|
299
|
+
["walk","doctype",1,"accept","",""],
|
|
300
|
+
["walk","doctype",1,"reject","",""],
|
|
301
|
+
["walk","doctype",1,"skip","",""],
|
|
302
|
+
["walk","doctype",1,"alternating","",""],
|
|
303
|
+
["walk","doctype",4,"accept","",""],
|
|
304
|
+
["walk","doctype",4,"reject","",""],
|
|
305
|
+
["walk","doctype",4,"skip","",""],
|
|
306
|
+
["walk","doctype",4,"alternating","",""],
|
|
307
|
+
["walk","doctype",128,"accept","",""],
|
|
308
|
+
["walk","doctype",128,"reject","",""],
|
|
309
|
+
["walk","doctype",128,"skip","",""],
|
|
310
|
+
["walk","doctype",128,"alternating","",""],
|
|
311
|
+
["walk","doctype",4294967295,"accept","10","10"],
|
|
312
|
+
["walk","doctype",4294967295,"reject","",""],
|
|
313
|
+
["walk","doctype",4294967295,"skip","",""],
|
|
314
|
+
["walk","doctype",4294967295,"alternating","10","10"]
|
|
315
|
+
]
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
// Host scripting and the style-read counter stay outside the test runner.
|
|
2
|
+
import { strict as assert } from "node:assert";
|
|
3
|
+
import { createPageRenderer } from "../../lib/renderer/page-renderer.js";
|
|
4
|
+
import { nodeOf } from "../../lib/renderer/bindings.js";
|
|
5
|
+
import { StyleEngine } from "../../lib/style/computed-style.js";
|
|
6
|
+
|
|
7
|
+
const font = await Bun.file(new URL("../../usr/share/fonts/DejaVuSansMono.ttf", import.meta.url)).bytes();
|
|
8
|
+
const sleep = Bun.sleep;
|
|
9
|
+
let finishFont;
|
|
10
|
+
const fontResponse = new Promise(resolve => { finishFont = resolve; });
|
|
11
|
+
const renderer = await createPageRenderer({
|
|
12
|
+
fetchResource: async url => ({ ok: false, status: 404, url, body: new Uint8Array() }),
|
|
13
|
+
pageFetch: () => fontResponse,
|
|
14
|
+
});
|
|
15
|
+
const evaluate = code => renderer.evaluate(code);
|
|
16
|
+
const eventually = async (code, check, message) => {
|
|
17
|
+
for (let attempt = 0; attempt < 100; attempt++) {
|
|
18
|
+
const value = await evaluate(code);
|
|
19
|
+
if (check(value)) return value;
|
|
20
|
+
await sleep(10);
|
|
21
|
+
}
|
|
22
|
+
assert.fail(message);
|
|
23
|
+
};
|
|
24
|
+
const originalGet = StyleEngine.prototype.get;
|
|
25
|
+
try {
|
|
26
|
+
renderer.resize(400, 300, 1);
|
|
27
|
+
renderer.setScripting("host");
|
|
28
|
+
await renderer.loadDocument({ url: "https://observer.test/", contentType: "text/html", source: `<!doctype html>
|
|
29
|
+
<style id=sheet>body{margin:0}#box{width:100px;height:50px}
|
|
30
|
+
body[data-wide] #box{width:175px}
|
|
31
|
+
#viewport{width:50%;height:10px}
|
|
32
|
+
#scroller{position:absolute;top:80px;width:100px;height:100px;overflow:auto}
|
|
33
|
+
#inside{position:absolute;top:150px;width:20px;height:20px}
|
|
34
|
+
#below{position:absolute;top:500px;width:20px;height:20px}</style>
|
|
35
|
+
<div id=box></div><div id=viewport></div>
|
|
36
|
+
<div id=scroller><div style="position:relative;height:300px"><div id=inside></div></div></div>
|
|
37
|
+
<div id=below></div><div style="height:1000px"></div>` });
|
|
38
|
+
await renderer.whenLoaded();
|
|
39
|
+
await evaluate(`window.box=document.getElementById('box');window.sizes=[];window.intersections=[];
|
|
40
|
+
window.ro=new ResizeObserver(entries=>sizes.push(entries[0].contentRect.width));ro.observe(box);
|
|
41
|
+
window.io=new IntersectionObserver(entries=>intersections.push(entries[0].isIntersecting));io.observe(box);`);
|
|
42
|
+
await eventually("sizes.length===1&&intersections.length===1", Boolean, "initial observers should be delivered");
|
|
43
|
+
// Consume initial focus bookkeeping, then count actual style measurements
|
|
44
|
+
// of the observed box. Reading body geometry itself cannot touch this box.
|
|
45
|
+
await evaluate("document.body.setAttribute('data-warm','1');document.body.clientWidth");
|
|
46
|
+
await sleep(40);
|
|
47
|
+
const target = nodeOf(globalThis.document.getElementById("box"));
|
|
48
|
+
let measurements = 0;
|
|
49
|
+
StyleEngine.prototype.get = function (element) {
|
|
50
|
+
if (element === target) measurements++;
|
|
51
|
+
return originalGet.call(this, element);
|
|
52
|
+
};
|
|
53
|
+
for (let i = 0; i < 5; i++) {
|
|
54
|
+
await evaluate(`document.body.setAttribute('data-counter','${i}');document.body.clientWidth`);
|
|
55
|
+
await sleep(30);
|
|
56
|
+
}
|
|
57
|
+
assert.equal(measurements, 0, "metadata flushes must not remeasure unchanged observer targets");
|
|
58
|
+
assert.deepEqual(await evaluate("[sizes,intersections]"), [[100], [true]]);
|
|
59
|
+
StyleEngine.prototype.get = originalGet;
|
|
60
|
+
|
|
61
|
+
// observe() itself still queues a first measurement without a new version.
|
|
62
|
+
await evaluate("window.newSizes=[];window.newRO=new ResizeObserver(e=>newSizes.push(e[0].contentRect.width));newRO.observe(box)");
|
|
63
|
+
await eventually("newSizes[0]", value => value === 100, "new observers must not wait for invalidation");
|
|
64
|
+
await evaluate("box.style.width='150px';document.body.clientWidth");
|
|
65
|
+
await eventually("sizes.at(-1)", value => value === 150, "inline size changes must notify");
|
|
66
|
+
await evaluate("box.style.width='';document.body.setAttribute('data-wide','');document.body.clientWidth");
|
|
67
|
+
await eventually("sizes.at(-1)", value => value === 175, "selector-observed metadata must still invalidate");
|
|
68
|
+
await evaluate("document.getElementById('sheet').textContent+='body[data-wide] #box{width:190px}';document.body.clientWidth");
|
|
69
|
+
await eventually("sizes.at(-1)", value => value === 190, "stylesheet changes must notify");
|
|
70
|
+
|
|
71
|
+
await evaluate(`window.insideChanges=[];window.belowChanges=[];window.viewportSizes=[];window.deviceSizes=[];
|
|
72
|
+
window.insideIO=new IntersectionObserver(e=>insideChanges.push(e[0].isIntersecting));insideIO.observe(document.getElementById('inside'));
|
|
73
|
+
window.belowIO=new IntersectionObserver(e=>belowChanges.push(e[0].isIntersecting));belowIO.observe(document.getElementById('below'));
|
|
74
|
+
window.viewportRO=new ResizeObserver(e=>viewportSizes.push(e[0].contentRect.width));viewportRO.observe(document.getElementById('viewport'));
|
|
75
|
+
window.deviceRO=new ResizeObserver(e=>deviceSizes.push(e[0].devicePixelContentBoxSize[0].inlineSize));deviceRO.observe(box,{box:'device-pixel-content-box'});`);
|
|
76
|
+
await eventually("insideChanges.length===1&&belowChanges.length===1&&viewportSizes.length===1&&deviceSizes.length===1", Boolean,
|
|
77
|
+
"scroll/viewport observers should be initialized");
|
|
78
|
+
assert.deepEqual(await evaluate("[insideChanges,belowChanges,viewportSizes,deviceSizes]"), [[false], [false], [200], [190]]);
|
|
79
|
+
await evaluate("document.getElementById('scroller').scrollTop=100");
|
|
80
|
+
await eventually("insideChanges.at(-1)", Boolean, "element scrolling must notify");
|
|
81
|
+
renderer.scrollTo(0, 450);
|
|
82
|
+
await eventually("belowChanges.at(-1)", Boolean, "viewport scrolling must notify");
|
|
83
|
+
renderer.scrollTo(0, 0);
|
|
84
|
+
renderer.resize(400, 300, 2);
|
|
85
|
+
await eventually("deviceSizes.at(-1)", value => value === 380, "DPR-only changes must notify without layout invalidation");
|
|
86
|
+
renderer.resize(600, 300, 2);
|
|
87
|
+
await eventually("viewportSizes.at(-1)", value => value === 300, "viewport resizing must notify");
|
|
88
|
+
await evaluate("box.remove();document.body.clientWidth");
|
|
89
|
+
await eventually("sizes.at(-1)===0&&intersections.at(-1)===false", Boolean, "removing a target must notify");
|
|
90
|
+
await evaluate("document.body.prepend(box);document.body.clientWidth");
|
|
91
|
+
await eventually("sizes.at(-1)===190&&intersections.at(-1)===true", Boolean, "reattaching a target must notify");
|
|
92
|
+
|
|
93
|
+
// Font completion is a resource invalidation even with no DOM mutation.
|
|
94
|
+
await evaluate(`document.body.insertAdjacentHTML('beforeend',
|
|
95
|
+
'<span id=text style="display:inline-block;font-family:ObserverMono,sans-serif;font-size:30px">iiii</span>')`);
|
|
96
|
+
await evaluate("window.fontSizes=[];window.fontRO=new ResizeObserver(e=>fontSizes.push(e[0].contentRect.width));fontRO.observe(document.getElementById('text'))");
|
|
97
|
+
const before = await eventually("fontSizes[0]", value => value > 0, "fallback font should be measured");
|
|
98
|
+
await evaluate(`document.getElementById('sheet').textContent+='@font-face{font-family:ObserverMono;src:url(/font.ttf)}';document.body.clientWidth`);
|
|
99
|
+
finishFont({ status: 200, body: font });
|
|
100
|
+
await eventually("fontSizes.at(-1)", value => value > before * 2, "loaded font metrics must notify");
|
|
101
|
+
process.stdout.write("observer invalidation integration passed\n");
|
|
102
|
+
} finally {
|
|
103
|
+
StyleEngine.prototype.get = originalGet;
|
|
104
|
+
renderer.dispose();
|
|
105
|
+
}
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
// Host scripting installs browser globals, so each scenario runs separately.
|
|
2
|
+
import {strict as assert} from 'node:assert';
|
|
3
|
+
import {createPageRenderer} from '../../lib/renderer/page-renderer.js';
|
|
4
|
+
|
|
5
|
+
const scenario = process.argv[2];
|
|
6
|
+
const frames = [], errors = [], videos = [];
|
|
7
|
+
let finishScript;
|
|
8
|
+
const pendingScript = new Promise(resolve => { finishScript = resolve; });
|
|
9
|
+
const renderer = await createPageRenderer({
|
|
10
|
+
renderMinInterval: 180,
|
|
11
|
+
onFrame: ({tile}) => frames.push(tile),
|
|
12
|
+
onVideo: message => videos.push(message),
|
|
13
|
+
reportError: error => errors.push(String(error)),
|
|
14
|
+
fetchResource: async url => {
|
|
15
|
+
if (url.endsWith('slow.js')) await pendingScript;
|
|
16
|
+
return {ok:true, url, body:new Uint8Array([32]), contentType:url.endsWith('.js') ? 'text/javascript' : 'video/mp4'};
|
|
17
|
+
},
|
|
18
|
+
videoDecoder: {
|
|
19
|
+
probe: async () => ({duration:10, video:{width:20, height:12}}),
|
|
20
|
+
decode: async () => ({width:20, height:12, fps:30,
|
|
21
|
+
nextFrame(w,h) { return new Uint8Array(w*h*4).fill(255); }, seek() {}, close() {}}),
|
|
22
|
+
},
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
try {
|
|
26
|
+
renderer.resize(64,36,1);
|
|
27
|
+
renderer.setScripting('host');
|
|
28
|
+
frames.length = 0;
|
|
29
|
+
const loading = scenario.startsWith('loading');
|
|
30
|
+
const media = scenario === 'video';
|
|
31
|
+
await renderer.loadDocument({url:'https://prepaint.test/', contentType:'text/html',
|
|
32
|
+
source:'<style>body{margin:0}#marker{height:500px;background:red}video{display:block;width:20px;height:12px}</style>'
|
|
33
|
+
+ (media ? '<video id=v src="clip.mp4"></video>' : '') + '<div id=marker></div>'
|
|
34
|
+
+ (loading ? '<script src="slow.js"></script>' : '')});
|
|
35
|
+
|
|
36
|
+
if (loading) {
|
|
37
|
+
await Bun.sleep(400);
|
|
38
|
+
assert(frames.length > 0);
|
|
39
|
+
assert(frames.every(tile => tile.height === 36), 'pending scripts must not expand an already visible tile');
|
|
40
|
+
if (scenario === 'loading-scroll') {
|
|
41
|
+
renderer.applyCompositorScroll(0,140);
|
|
42
|
+
await Bun.sleep(90);
|
|
43
|
+
assert(frames.some(tile => tile.y <= 140 && tile.y+tile.height >= 176),
|
|
44
|
+
'scrolling beyond the old tile must still paint while loading');
|
|
45
|
+
}
|
|
46
|
+
finishScript();
|
|
47
|
+
await renderer.whenLoaded();
|
|
48
|
+
// The script has no visual effect: refresh need not call render. The
|
|
49
|
+
// deferred prepaint still needs to wake and expand the unchanged tile.
|
|
50
|
+
await Bun.sleep(550);
|
|
51
|
+
assert(frames.some(tile => tile.height > 36), 'prepaint resumes after a no-op load settles');
|
|
52
|
+
} else {
|
|
53
|
+
await renderer.whenLoaded();
|
|
54
|
+
if (media) {
|
|
55
|
+
await renderer.evaluate('document.getElementById("v").play()');
|
|
56
|
+
await Bun.sleep(400);
|
|
57
|
+
assert(videos.some(message => message.type === 'video-open'));
|
|
58
|
+
assert(frames.some(tile => tile.height > 36 && tile.videoLayer),
|
|
59
|
+
'independent video playback alone must not block idle prepaint');
|
|
60
|
+
} else {
|
|
61
|
+
await renderer.evaluate('setTimeout(()=>document.documentElement.setAttribute("data-bookkeeping","changed"),1)');
|
|
62
|
+
await Bun.sleep(80);
|
|
63
|
+
assert(frames.every(tile => tile.height === 36), 'pending frame takes priority over speculative raster');
|
|
64
|
+
await Bun.sleep(550);
|
|
65
|
+
assert(frames.some(tile => tile.height > 36), 'prepaint resumes after a queued no-op frame');
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
assert.deepEqual(errors, []);
|
|
69
|
+
process.stdout.write(`prepaint ${scenario} passed\n`);
|
|
70
|
+
} finally {
|
|
71
|
+
finishScript();
|
|
72
|
+
renderer.dispose();
|
|
73
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
// Host scripting is isolated from the test runner's globals.
|
|
2
|
+
import { strict as assert } from "node:assert";
|
|
3
|
+
import { createPageRenderer } from "../../lib/renderer/page-renderer.js";
|
|
4
|
+
import { nodeOf } from "../../lib/renderer/bindings.js";
|
|
5
|
+
const renderer = await createPageRenderer({
|
|
6
|
+
fetchResource: async url => ({ ok: false, status: 404, url, body: new Uint8Array() }),
|
|
7
|
+
});
|
|
8
|
+
try {
|
|
9
|
+
renderer.resize(400, 300, 1);
|
|
10
|
+
renderer.setScripting("host");
|
|
11
|
+
await renderer.loadDocument({ url: "https://resource.test/", contentType: "text/html",
|
|
12
|
+
source: '<!doctype html><style>body{color:red}</style><body>' + '<div>Item</div>'.repeat(100) + '</body>' });
|
|
13
|
+
await renderer.whenLoaded();
|
|
14
|
+
await renderer.evaluate("document.body.setAttribute('data-warm','1');document.body.clientWidth");
|
|
15
|
+
const document = nodeOf(globalThis.document), query = document.querySelectorAll.bind(document);
|
|
16
|
+
const watched = new Set(["video, audio", "img", "style, link[rel~='stylesheet']", "iframe"]);
|
|
17
|
+
const queries = [];
|
|
18
|
+
document.querySelectorAll = selector => {
|
|
19
|
+
if (watched.has(selector)) queries.push(selector);
|
|
20
|
+
return query(selector);
|
|
21
|
+
};
|
|
22
|
+
await renderer.evaluate(`for(let i=0;i<30;i++) {
|
|
23
|
+
document.body.setAttribute('data-counter',String(i));
|
|
24
|
+
document.body.setAttribute('aria-label',String(i));
|
|
25
|
+
document.body.clientWidth;
|
|
26
|
+
}`);
|
|
27
|
+
assert.deepEqual(queries, [], "metadata-only geometry flushes retain resource discovery");
|
|
28
|
+
await renderer.evaluate("document.body.insertAdjacentHTML('beforeend','<video></video>');document.body.clientWidth");
|
|
29
|
+
assert.ok(queries.includes("video, audio"), "structural insertion refreshes media discovery synchronously");
|
|
30
|
+
assert.equal(await renderer.evaluate("document.querySelector('video').offsetWidth"), 300);
|
|
31
|
+
process.stdout.write("resource discovery integration passed\n");
|
|
32
|
+
} finally { renderer.dispose(); }
|