Skip to content

Commit

Permalink
fix: remote CSS does not get rebuilt properly (#226)
Browse files Browse the repository at this point in the history
This fixes an issue where inlined CSS from a remotely loaded `<link>`
does not get applied properly due to object reference mutation.
  • Loading branch information
billyvg authored Dec 10, 2024
1 parent 542e0d0 commit ba5a067
Show file tree
Hide file tree
Showing 4 changed files with 185 additions and 10 deletions.
25 changes: 15 additions & 10 deletions packages/rrweb/src/replay/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1835,17 +1835,22 @@ export class Replayer {
const newSn = mirror.getMeta(
target as Node & RRNode,
) as serializedElementNodeWithId;
Object.assign(
newSn.attributes,
mutation.attributes as attributes,
const newNode = buildNodeWithSN(
{
...newSn,
attributes: {
...newSn.attributes,
...(mutation.attributes as attributes),
},
},
{
doc: target.ownerDocument as Document, // can be Document or RRDocument
mirror: mirror as Mirror,
skipChild: true,
hackCss: true,
cache: this.cache,
},
);
const newNode = buildNodeWithSN(newSn, {
doc: target.ownerDocument as Document, // can be Document or RRDocument
mirror: mirror as Mirror,
skipChild: true,
hackCss: true,
cache: this.cache,
});
const siblingNode = target.nextSibling;
const parentNode = target.parentNode;
if (newNode && parentNode) {
Expand Down
60 changes: 60 additions & 0 deletions packages/rrweb/test/__snapshots__/replayer.test.ts.snap
Original file line number Diff line number Diff line change
Expand Up @@ -144,6 +144,66 @@ file-cid-3
"
`;
exports[`replayer can handle remote stylesheets 1`] = `
"file-frame-2
<html>
<head>
<meta http-equiv=\\"Content-Type\\" content=\\"text/html; charset=UTF-8\\" />
</head>
<body>
<div class=\\"replayer-wrapper\\">
<iframe
sandbox=\\"allow-same-origin\\"
scrolling=\\"no\\"
width=\\"1000\\"
height=\\"800\\"
style=\\"display: inherit; pointer-events: none\\"
></iframe>
</div>
</body>
</html>
file-frame-3
<html>
<head>
<meta http-equiv=\\"Content-Type\\" content=\\"text/html; charset=UTF-8\\" />
<link rel=\\"stylesheet\\" type=\\"text/css\\" href=\\"file-cid-0\\" />
<link rel=\\"stylesheet\\" type=\\"text/css\\" href=\\"file-cid-1\\" />
</head>
<body></body>
</html>
file-cid-0
@charset \\"utf-8\\";
.rr-block { background: currentcolor; }
noscript { display: none !important; }
html.rrweb-paused *, html.rrweb-paused ::before, html.rrweb-paused ::after { animation-play-state: paused !important; }
file-cid-1
@charset \\"utf-8\\";
.OverlayDrawer-modal-187 { }
.OverlayDrawer-paper-188 { width: 100%; }
@media (min-width: 48em) {
.OverlayDrawer-paper-188 { width: 38rem; }
}
@media (min-width: 48em) {
}
@media (min-width: 48em) {
}
"
`;

exports[`replayer can handle removing style elements 1`] = `
"file-frame-1
<html>
Expand Down
18 changes: 18 additions & 0 deletions packages/rrweb/test/replayer.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import {
launchPuppeteer,
sampleEvents as events,
sampleStyleSheetRemoveEvents as stylesheetRemoveEvents,
sampleRemoteStyleSheetEvents as remoteStyleSheetEvents,
waitForRAF,
} from './utils';
import styleSheetRuleEvents from './events/style-sheet-rule-events';
Expand Down Expand Up @@ -210,6 +211,23 @@ describe('replayer', function () {
await assertDomSnapshot(page);
});

it('can handle remote stylesheets', async () => {
await page.evaluate(`events = ${JSON.stringify(remoteStyleSheetEvents)}`);
const actionLength = await page.evaluate(`
const { Replayer } = rrweb;
const replayer = new Replayer(events);
replayer.play(2500);
replayer['timer']['actions'].length;
`);
expect(actionLength).toEqual(
remoteStyleSheetEvents.filter(
(e) => e.timestamp - remoteStyleSheetEvents[0].timestamp >= 2500,
).length,
);

await assertDomSnapshot(page);
});

it('can fast forward selection events', async () => {
await page.evaluate(`events = ${JSON.stringify(selectionEvents)}`);

Expand Down
92 changes: 92 additions & 0 deletions packages/rrweb/test/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -559,6 +559,98 @@ export const sampleStyleSheetRemoveEvents: eventWithTime[] = [
},
];

export const sampleRemoteStyleSheetEvents: eventWithTime[] = [
{
type: EventType.DomContentLoaded,
data: {},
timestamp: now,
},
{
type: EventType.Load,
data: {},
timestamp: now + 1000,
},
{
type: EventType.Meta,
data: {
href: 'http://localhost',
width: 1000,
height: 800,
},
timestamp: now + 1000,
},
{
type: EventType.FullSnapshot,
data: {
node: {
type: 0,
childNodes: [
{
type: 2,
tagName: 'html',
attributes: {},
childNodes: [
{
type: 2,
tagName: 'head',
attributes: {},
childNodes: [
{
type: 2,
tagName: 'link',
attributes: {
rel: 'stylesheet',
href: '',
},
childNodes: [],
id: 4,
},
],
id: 3,
},
{
type: 2,
tagName: 'body',
attributes: {},
childNodes: [],
id: 6,
},
],
id: 2,
},
],
id: 1,
},
initialOffset: {
top: 0,
left: 0,
},
},
timestamp: now + 1000,
},
{
type: EventType.IncrementalSnapshot,
data: {
source: IncrementalSource.Mutation,
texts: [],
attributes: [
{
id: 4,
attributes: {
href: null,
rel: null,
_cssText:
'.OverlayDrawer-modal-187 { }.OverlayDrawer-paper-188 { width: 100%; }@media (min-width: 48em) {\n .OverlayDrawer-paper-188 { width: 38rem; }\n}@media (min-width: 48em) {\n}@media (min-width: 48em) {\n}',
},
},
],
removes: [],
adds: [],
},
timestamp: now + 2000,
},
];

export const polyfillWebGLGlobals = () => {
// polyfill as jsdom does not have support for these classes
// consider replacing with https://www.npmjs.com/package/canvas
Expand Down

0 comments on commit ba5a067

Please sign in to comment.