Fix missed adopted style sheets of shadow doms in checkout full snapshot (#1086)
* fix: adoptedStyleSheets in shadow doms are missed when a full snapshot is checked out after recording has started * fix: avoid removing monkey patch of all existed shadow doms when take a new full snapshot * Apply formatting changes * Update packages/rrweb/test/record.test.ts Co-authored-by: Justin Halsall <Juice10@users.noreply.github.com> * fix typo * update outdated snapshot Co-authored-by: Justin Halsall <Juice10@users.noreply.github.com>
This commit is contained in:
@@ -335,6 +335,8 @@ function record<T = eventWithTime>(
|
||||
|
||||
// When we take a full snapshot, old tracked StyleSheets need to be removed.
|
||||
stylesheetManager.reset();
|
||||
// Old shadow doms cache need to be cleared.
|
||||
shadowDomManager.clearCache();
|
||||
|
||||
mutationBuffers.forEach((buf) => buf.lock()); // don't allow any mirror modifications during snapshotting
|
||||
const node = snapshot(document, {
|
||||
|
||||
@@ -129,8 +129,12 @@ export class ShadowDomManager {
|
||||
}
|
||||
}
|
||||
|
||||
public reset() {
|
||||
this.restorePatches.forEach((restorePatch) => restorePatch());
|
||||
public clearCache() {
|
||||
this.shadowDoms = new WeakSet();
|
||||
}
|
||||
|
||||
public reset() {
|
||||
this.restorePatches.forEach((restorePatch) => restorePatch());
|
||||
this.clearCache();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1059,6 +1059,211 @@ exports[`record captures adopted stylesheets in shadow doms and iframe 1`] = `
|
||||
]"
|
||||
`;
|
||||
|
||||
exports[`record captures adopted stylesheets of shadow doms in checkout full snapshot 1`] = `
|
||||
"[
|
||||
{
|
||||
\\"type\\": 4,
|
||||
\\"data\\": {
|
||||
\\"href\\": \\"about:blank\\",
|
||||
\\"width\\": 1920,
|
||||
\\"height\\": 1080
|
||||
}
|
||||
},
|
||||
{
|
||||
\\"type\\": 2,
|
||||
\\"data\\": {
|
||||
\\"node\\": {
|
||||
\\"type\\": 0,
|
||||
\\"childNodes\\": [
|
||||
{
|
||||
\\"type\\": 1,
|
||||
\\"name\\": \\"html\\",
|
||||
\\"publicId\\": \\"\\",
|
||||
\\"systemId\\": \\"\\",
|
||||
\\"id\\": 2
|
||||
},
|
||||
{
|
||||
\\"type\\": 2,
|
||||
\\"tagName\\": \\"html\\",
|
||||
\\"attributes\\": {},
|
||||
\\"childNodes\\": [
|
||||
{
|
||||
\\"type\\": 2,
|
||||
\\"tagName\\": \\"head\\",
|
||||
\\"attributes\\": {},
|
||||
\\"childNodes\\": [],
|
||||
\\"id\\": 4
|
||||
},
|
||||
{
|
||||
\\"type\\": 2,
|
||||
\\"tagName\\": \\"body\\",
|
||||
\\"attributes\\": {},
|
||||
\\"childNodes\\": [
|
||||
{
|
||||
\\"type\\": 3,
|
||||
\\"textContent\\": \\"\\\\n \\",
|
||||
\\"id\\": 6
|
||||
},
|
||||
{
|
||||
\\"type\\": 2,
|
||||
\\"tagName\\": \\"div\\",
|
||||
\\"attributes\\": {
|
||||
\\"id\\": \\"shadow-host-1\\"
|
||||
},
|
||||
\\"childNodes\\": [
|
||||
{
|
||||
\\"type\\": 3,
|
||||
\\"textContent\\": \\"entry\\",
|
||||
\\"id\\": 8
|
||||
}
|
||||
],
|
||||
\\"id\\": 7,
|
||||
\\"isShadowHost\\": true
|
||||
},
|
||||
{
|
||||
\\"type\\": 3,
|
||||
\\"textContent\\": \\"\\\\n \\",
|
||||
\\"id\\": 9
|
||||
}
|
||||
],
|
||||
\\"id\\": 5
|
||||
}
|
||||
],
|
||||
\\"id\\": 3
|
||||
}
|
||||
],
|
||||
\\"id\\": 1
|
||||
},
|
||||
\\"initialOffset\\": {
|
||||
\\"left\\": 0,
|
||||
\\"top\\": 0
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
\\"type\\": 3,
|
||||
\\"data\\": {
|
||||
\\"source\\": 15,
|
||||
\\"id\\": 7,
|
||||
\\"styleIds\\": [
|
||||
1
|
||||
],
|
||||
\\"styles\\": [
|
||||
{
|
||||
\\"styleId\\": 1,
|
||||
\\"rules\\": [
|
||||
{
|
||||
\\"rule\\": \\"h1 { color: blue; }\\",
|
||||
\\"index\\": 0
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
\\"type\\": 4,
|
||||
\\"data\\": {
|
||||
\\"href\\": \\"about:blank\\",
|
||||
\\"width\\": 1920,
|
||||
\\"height\\": 1080
|
||||
}
|
||||
},
|
||||
{
|
||||
\\"type\\": 2,
|
||||
\\"data\\": {
|
||||
\\"node\\": {
|
||||
\\"type\\": 0,
|
||||
\\"childNodes\\": [
|
||||
{
|
||||
\\"type\\": 1,
|
||||
\\"name\\": \\"html\\",
|
||||
\\"publicId\\": \\"\\",
|
||||
\\"systemId\\": \\"\\",
|
||||
\\"id\\": 2
|
||||
},
|
||||
{
|
||||
\\"type\\": 2,
|
||||
\\"tagName\\": \\"html\\",
|
||||
\\"attributes\\": {},
|
||||
\\"childNodes\\": [
|
||||
{
|
||||
\\"type\\": 2,
|
||||
\\"tagName\\": \\"head\\",
|
||||
\\"attributes\\": {},
|
||||
\\"childNodes\\": [],
|
||||
\\"id\\": 4
|
||||
},
|
||||
{
|
||||
\\"type\\": 2,
|
||||
\\"tagName\\": \\"body\\",
|
||||
\\"attributes\\": {},
|
||||
\\"childNodes\\": [
|
||||
{
|
||||
\\"type\\": 3,
|
||||
\\"textContent\\": \\"\\\\n \\",
|
||||
\\"id\\": 6
|
||||
},
|
||||
{
|
||||
\\"type\\": 2,
|
||||
\\"tagName\\": \\"div\\",
|
||||
\\"attributes\\": {
|
||||
\\"id\\": \\"shadow-host-1\\"
|
||||
},
|
||||
\\"childNodes\\": [
|
||||
{
|
||||
\\"type\\": 3,
|
||||
\\"textContent\\": \\"entry\\",
|
||||
\\"id\\": 8
|
||||
}
|
||||
],
|
||||
\\"id\\": 7,
|
||||
\\"isShadowHost\\": true
|
||||
},
|
||||
{
|
||||
\\"type\\": 3,
|
||||
\\"textContent\\": \\"\\\\n \\",
|
||||
\\"id\\": 9
|
||||
}
|
||||
],
|
||||
\\"id\\": 5
|
||||
}
|
||||
],
|
||||
\\"id\\": 3
|
||||
}
|
||||
],
|
||||
\\"id\\": 1
|
||||
},
|
||||
\\"initialOffset\\": {
|
||||
\\"left\\": 0,
|
||||
\\"top\\": 0
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
\\"type\\": 3,
|
||||
\\"data\\": {
|
||||
\\"source\\": 15,
|
||||
\\"id\\": 7,
|
||||
\\"styleIds\\": [
|
||||
1
|
||||
],
|
||||
\\"styles\\": [
|
||||
{
|
||||
\\"styleId\\": 1,
|
||||
\\"rules\\": [
|
||||
{
|
||||
\\"rule\\": \\"h1 { color: blue; }\\",
|
||||
\\"index\\": 0
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
]"
|
||||
`;
|
||||
|
||||
exports[`record captures inserted style text nodes correctly 1`] = `
|
||||
"[
|
||||
{
|
||||
|
||||
@@ -29,9 +29,12 @@ interface ISuite {
|
||||
|
||||
interface IWindow extends Window {
|
||||
rrweb: {
|
||||
record: (
|
||||
record: ((
|
||||
options: recordOptions<eventWithTime>,
|
||||
) => listenerHandler | undefined;
|
||||
) => listenerHandler | undefined) & {
|
||||
takeFullSnapshot: (isCheckout?: boolean | undefined) => void;
|
||||
};
|
||||
|
||||
addCustomEvent<T>(tag: string, payload: T): void;
|
||||
};
|
||||
emit: (e: eventWithTime) => undefined;
|
||||
@@ -491,9 +494,9 @@ describe('record', function (this: ISuite) {
|
||||
iframe!.contentDocument!.adoptedStyleSheets = [sheet2];
|
||||
iframe!.contentDocument!.body.innerHTML = '<h1>h1 in iframe</h1>';
|
||||
|
||||
const { record } = ((window as unknown) as IWindow).rrweb;
|
||||
record({
|
||||
emit: ((window as unknown) as IWindow).emit,
|
||||
const { rrweb, emit } = (window as unknown) as IWindow;
|
||||
rrweb.record({
|
||||
emit,
|
||||
});
|
||||
|
||||
setTimeout(() => {
|
||||
@@ -565,9 +568,9 @@ describe('record', function (this: ISuite) {
|
||||
sheet2.replaceSync!('div {font-size: large;}');
|
||||
shadowHost.shadowRoot!.adoptedStyleSheets = [sheet2];
|
||||
|
||||
const { record } = ((window as unknown) as IWindow).rrweb;
|
||||
record({
|
||||
emit: ((window as unknown) as IWindow).emit,
|
||||
const { rrweb, emit } = (window as unknown) as IWindow;
|
||||
rrweb.record({
|
||||
emit,
|
||||
});
|
||||
|
||||
setTimeout(() => {
|
||||
@@ -586,6 +589,35 @@ describe('record', function (this: ISuite) {
|
||||
assertSnapshot(ctx.events);
|
||||
});
|
||||
|
||||
it('captures adopted stylesheets of shadow doms in checkout full snapshot', async () => {
|
||||
await ctx.page.evaluate(() => {
|
||||
return new Promise((resolve) => {
|
||||
document.body.innerHTML = `
|
||||
<div id="shadow-host-1">entry</div>
|
||||
`;
|
||||
|
||||
let shadowHost = document.querySelector('div')!;
|
||||
shadowHost!.attachShadow({ mode: 'open' });
|
||||
const sheet = new CSSStyleSheet();
|
||||
sheet.replaceSync!('h1 {color: blue;}');
|
||||
shadowHost.shadowRoot!.adoptedStyleSheets = [sheet];
|
||||
|
||||
const { rrweb, emit } = (window as unknown) as IWindow;
|
||||
rrweb.record({
|
||||
emit,
|
||||
});
|
||||
|
||||
setTimeout(() => {
|
||||
// When a full snapshot is checked out manually, all adoptedStylesheets should also be captured.
|
||||
rrweb.record.takeFullSnapshot(true);
|
||||
resolve(undefined);
|
||||
}, 10);
|
||||
});
|
||||
});
|
||||
await waitForRAF(ctx.page);
|
||||
assertSnapshot(ctx.events);
|
||||
});
|
||||
|
||||
it('captures stylesheets in iframes with `blob:` url', async () => {
|
||||
await ctx.page.evaluate(() => {
|
||||
const iframe = document.createElement('iframe');
|
||||
|
||||
Reference in New Issue
Block a user