@specific.dev/spectest 0.73.0 → 0.73.1
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/dist/browser.js +26 -6
- package/dist/vendor/rrweb-record.min.js +4 -4
- package/package.json +1 -1
- package/src/browser.ts +26 -6
- package/src/vendor/rrweb-record.min.js +4 -4
package/dist/browser.js
CHANGED
|
@@ -236,11 +236,17 @@ async function applySafeAreaInsets(cdp, d) {
|
|
|
236
236
|
// ────────────────────────────────────────────────────────────────────────
|
|
237
237
|
// rrweb bootstrap
|
|
238
238
|
// ────────────────────────────────────────────────────────────────────────
|
|
239
|
-
// Vendored `@rrweb/record` UMD bundle (rrweb 2.x)
|
|
240
|
-
//
|
|
241
|
-
//
|
|
242
|
-
//
|
|
243
|
-
//
|
|
239
|
+
// Vendored `@rrweb/record` UMD bundle (rrweb 2.x), BUILT FROM OUR FORK by
|
|
240
|
+
// `scripts/rrweb-record/build.sh` (specific-dev/rrweb, branch `spectest` =
|
|
241
|
+
// the `rrweb@2.1.0` tag + upstream PR 1802). Never copy the npm dist over
|
|
242
|
+
// it: upstream's `@rrweb/utils` lists `querySelector` as an accessor, so its
|
|
243
|
+
// pristine-prototype test always fails and `record()` appends an <iframe>
|
|
244
|
+
// to every document — measured at ~1 s per navigation (see the top-frame
|
|
245
|
+
// gate on PAGE_INIT_SCRIPT below). Its global `rrwebRecord` is a module
|
|
246
|
+
// object — the record function is `rrwebRecord.record` (the bootstrap
|
|
247
|
+
// resolves both this and the legacy function-shaped global). Read once at
|
|
248
|
+
// module init — the SDK ships this file in the base snapshot so no network
|
|
249
|
+
// fetch happens inside the VM.
|
|
244
250
|
const RRWEB_BUNDLE = (() => {
|
|
245
251
|
try {
|
|
246
252
|
const here = path.dirname(fileURLToPath(import.meta.url));
|
|
@@ -711,8 +717,22 @@ const RRWEB_BOOTSTRAP = `
|
|
|
711
717
|
// call expression — the TypeError kills everything after the bundle, so
|
|
712
718
|
// the globals define but the bootstrap never runs (zero rrweb events,
|
|
713
719
|
// empty replays).
|
|
720
|
+
//
|
|
721
|
+
// The whole script runs in the TOP frame only. `Page.addScriptToEvaluateOnNewDocument`
|
|
722
|
+
// fires for every frame, and until 2026-09-06 that was a storm: rrweb's
|
|
723
|
+
// `getUntaintedPrototype` appends a hidden <iframe> to read pristine DOM
|
|
724
|
+
// prototypes, this script then ran inside it, the child's `record()` (which
|
|
725
|
+
// treats every frame as an emitting frame unless `recordCrossOriginIframes`
|
|
726
|
+
// is set) appended an iframe of its own, and so on to Chromium's frame-depth
|
|
727
|
+
// cap — 44 nested frames and 45 executions of ~180 KB of JS per navigation,
|
|
728
|
+
// ~0.8–1.3 s inside the page's `load` handler even for a one-line document
|
|
729
|
+
// (`goto` p50 was ~1.5 s in every project). The vendored bundle's fork patch
|
|
730
|
+
// removes the iframe on an unpatched page; this gate bounds it on a page
|
|
731
|
+
// whose prototypes really are patched (zone.js). Nothing is lost: rrweb
|
|
732
|
+
// records same-origin iframes from the parent, and a cross-origin child's
|
|
733
|
+
// events were never drained (they land in the child's own buffer).
|
|
714
734
|
const PAGE_INIT_SCRIPT = RRWEB_BUNDLE
|
|
715
|
-
?
|
|
735
|
+
? `if (window === window.top) {\n${RRWEB_BUNDLE}\n;\n${RRWEB_CONSOLE_PLUGIN_BUNDLE}\n;\n${RRWEB_BOOTSTRAP}\n}`
|
|
716
736
|
: "";
|
|
717
737
|
// Page-side expression that atomically swaps in a fresh buffer and
|
|
718
738
|
// returns the old one. Wrapped as a single expression so view.evaluate's
|
|
@@ -131,13 +131,13 @@ const testableAccessors = {
|
|
|
131
131
|
"ownerDocument"
|
|
132
132
|
],
|
|
133
133
|
ShadowRoot: ["host", "styleSheets"],
|
|
134
|
-
Element: ["shadowRoot"
|
|
134
|
+
Element: ["shadowRoot"],
|
|
135
135
|
MutationObserver: []
|
|
136
136
|
};
|
|
137
137
|
const testableMethods = {
|
|
138
138
|
Node: ["contains", "getRootNode"],
|
|
139
139
|
ShadowRoot: ["getSelection"],
|
|
140
|
-
Element: [],
|
|
140
|
+
Element: ["querySelector", "querySelectorAll"],
|
|
141
141
|
MutationObserver: ["constructor"]
|
|
142
142
|
};
|
|
143
143
|
const untaintedBasePrototype = {};
|
|
@@ -263,10 +263,10 @@ function shadowRoot(n) {
|
|
|
263
263
|
return getUntaintedAccessor("Element", n, "shadowRoot");
|
|
264
264
|
}
|
|
265
265
|
function querySelector(n, selectors) {
|
|
266
|
-
return
|
|
266
|
+
return getUntaintedMethod("Element", n, "querySelector")(selectors);
|
|
267
267
|
}
|
|
268
268
|
function querySelectorAll(n, selectors) {
|
|
269
|
-
return
|
|
269
|
+
return getUntaintedMethod("Element", n, "querySelectorAll")(selectors);
|
|
270
270
|
}
|
|
271
271
|
function mutationObserverCtor() {
|
|
272
272
|
var _a2;
|
package/package.json
CHANGED
package/src/browser.ts
CHANGED
|
@@ -615,11 +615,17 @@ async function applySafeAreaInsets(
|
|
|
615
615
|
// rrweb bootstrap
|
|
616
616
|
// ────────────────────────────────────────────────────────────────────────
|
|
617
617
|
|
|
618
|
-
// Vendored `@rrweb/record` UMD bundle (rrweb 2.x)
|
|
619
|
-
//
|
|
620
|
-
//
|
|
621
|
-
//
|
|
622
|
-
//
|
|
618
|
+
// Vendored `@rrweb/record` UMD bundle (rrweb 2.x), BUILT FROM OUR FORK by
|
|
619
|
+
// `scripts/rrweb-record/build.sh` (specific-dev/rrweb, branch `spectest` =
|
|
620
|
+
// the `rrweb@2.1.0` tag + upstream PR 1802). Never copy the npm dist over
|
|
621
|
+
// it: upstream's `@rrweb/utils` lists `querySelector` as an accessor, so its
|
|
622
|
+
// pristine-prototype test always fails and `record()` appends an <iframe>
|
|
623
|
+
// to every document — measured at ~1 s per navigation (see the top-frame
|
|
624
|
+
// gate on PAGE_INIT_SCRIPT below). Its global `rrwebRecord` is a module
|
|
625
|
+
// object — the record function is `rrwebRecord.record` (the bootstrap
|
|
626
|
+
// resolves both this and the legacy function-shaped global). Read once at
|
|
627
|
+
// module init — the SDK ships this file in the base snapshot so no network
|
|
628
|
+
// fetch happens inside the VM.
|
|
623
629
|
const RRWEB_BUNDLE: string = (() => {
|
|
624
630
|
try {
|
|
625
631
|
const here = path.dirname(fileURLToPath(import.meta.url));
|
|
@@ -1094,8 +1100,22 @@ const RRWEB_BOOTSTRAP = `
|
|
|
1094
1100
|
// call expression — the TypeError kills everything after the bundle, so
|
|
1095
1101
|
// the globals define but the bootstrap never runs (zero rrweb events,
|
|
1096
1102
|
// empty replays).
|
|
1103
|
+
//
|
|
1104
|
+
// The whole script runs in the TOP frame only. `Page.addScriptToEvaluateOnNewDocument`
|
|
1105
|
+
// fires for every frame, and until 2026-09-06 that was a storm: rrweb's
|
|
1106
|
+
// `getUntaintedPrototype` appends a hidden <iframe> to read pristine DOM
|
|
1107
|
+
// prototypes, this script then ran inside it, the child's `record()` (which
|
|
1108
|
+
// treats every frame as an emitting frame unless `recordCrossOriginIframes`
|
|
1109
|
+
// is set) appended an iframe of its own, and so on to Chromium's frame-depth
|
|
1110
|
+
// cap — 44 nested frames and 45 executions of ~180 KB of JS per navigation,
|
|
1111
|
+
// ~0.8–1.3 s inside the page's `load` handler even for a one-line document
|
|
1112
|
+
// (`goto` p50 was ~1.5 s in every project). The vendored bundle's fork patch
|
|
1113
|
+
// removes the iframe on an unpatched page; this gate bounds it on a page
|
|
1114
|
+
// whose prototypes really are patched (zone.js). Nothing is lost: rrweb
|
|
1115
|
+
// records same-origin iframes from the parent, and a cross-origin child's
|
|
1116
|
+
// events were never drained (they land in the child's own buffer).
|
|
1097
1117
|
const PAGE_INIT_SCRIPT = RRWEB_BUNDLE
|
|
1098
|
-
?
|
|
1118
|
+
? `if (window === window.top) {\n${RRWEB_BUNDLE}\n;\n${RRWEB_CONSOLE_PLUGIN_BUNDLE}\n;\n${RRWEB_BOOTSTRAP}\n}`
|
|
1099
1119
|
: "";
|
|
1100
1120
|
|
|
1101
1121
|
// Page-side expression that atomically swaps in a fresh buffer and
|
|
@@ -131,13 +131,13 @@ const testableAccessors = {
|
|
|
131
131
|
"ownerDocument"
|
|
132
132
|
],
|
|
133
133
|
ShadowRoot: ["host", "styleSheets"],
|
|
134
|
-
Element: ["shadowRoot"
|
|
134
|
+
Element: ["shadowRoot"],
|
|
135
135
|
MutationObserver: []
|
|
136
136
|
};
|
|
137
137
|
const testableMethods = {
|
|
138
138
|
Node: ["contains", "getRootNode"],
|
|
139
139
|
ShadowRoot: ["getSelection"],
|
|
140
|
-
Element: [],
|
|
140
|
+
Element: ["querySelector", "querySelectorAll"],
|
|
141
141
|
MutationObserver: ["constructor"]
|
|
142
142
|
};
|
|
143
143
|
const untaintedBasePrototype = {};
|
|
@@ -263,10 +263,10 @@ function shadowRoot(n) {
|
|
|
263
263
|
return getUntaintedAccessor("Element", n, "shadowRoot");
|
|
264
264
|
}
|
|
265
265
|
function querySelector(n, selectors) {
|
|
266
|
-
return
|
|
266
|
+
return getUntaintedMethod("Element", n, "querySelector")(selectors);
|
|
267
267
|
}
|
|
268
268
|
function querySelectorAll(n, selectors) {
|
|
269
|
-
return
|
|
269
|
+
return getUntaintedMethod("Element", n, "querySelectorAll")(selectors);
|
|
270
270
|
}
|
|
271
271
|
function mutationObserverCtor() {
|
|
272
272
|
var _a2;
|