diff --git a/.changeset/fix-img-srcset-attributes-dropped.md b/.changeset/fix-img-srcset-attributes-dropped.md new file mode 100644 index 00000000..1843d6bd --- /dev/null +++ b/.changeset/fix-img-srcset-attributes-dropped.md @@ -0,0 +1,5 @@ +--- +"rrweb-snapshot": patch +--- + +fix: rebuilding an `` with both `srcset` and an inlined `rr_dataURL` was silently dropping every other attribute (`class`, `alt`, `sizes`, `decoding`, `loading`, etc.) instead of only backing up `srcset`, because the guard only checked the node's attributes rather than which attribute was currently being applied diff --git a/packages/rrweb-snapshot/src/rebuild.ts b/packages/rrweb-snapshot/src/rebuild.ts index 57b5a4d8..d1fd27ab 100644 --- a/packages/rrweb-snapshot/src/rebuild.ts +++ b/packages/rrweb-snapshot/src/rebuild.ts @@ -321,14 +321,12 @@ function buildNode( // ignore } else if ( tagName === 'img' && - n.attributes.srcset && + name === 'srcset' && n.attributes.rr_dataURL ) { - // backup original img srcset - node.setAttribute( - 'rrweb-original-srcset', - n.attributes.srcset as string, - ); + // backup original img srcset; every other attribute on this + // element still needs to fall through to the `else` branch below + node.setAttribute('rrweb-original-srcset', value.toString()); } else { node.setAttribute(name, value.toString()); } diff --git a/packages/rrweb-snapshot/test/rebuild.test.ts b/packages/rrweb-snapshot/test/rebuild.test.ts index e72c466f..e06ef3d7 100644 --- a/packages/rrweb-snapshot/test/rebuild.test.ts +++ b/packages/rrweb-snapshot/test/rebuild.test.ts @@ -71,6 +71,47 @@ describe('rebuild', function () { ) as HTMLImageElement; expect(node?.src).toBe(dataURI); }); + + it('should keep other attributes when srcset is also backed up', function () { + // Regression test for https://github.com/highlight/highlight/issues/4853: + // when an img has both `srcset` and an inlined `rr_dataURL`, every + // attribute (not just `srcset`) was being replaced by a repeated + // `rrweb-original-srcset` backup instead of being set normally, + // because the `name === 'srcset'` check was missing from the branch + // condition (it only checked the *node's* attributes, not which + // attribute was currently being applied). + const dataURI = + 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNk+M9QDwADhgGAWjR9awAAAABJRU5ErkJggg=='; + const node = buildNodeWithSN( + { + id: 1, + tagName: 'img', + type: NodeType.Element, + attributes: { + class: 'logo', + alt: 'Logo', + sizes: '32px', + srcset: 'http://example.com/image.png 1x, http://example.com/image2x.png 2x', + rr_dataURL: dataURI, + src: 'http://example.com/image.png', + }, + childNodes: [], + }, + { + doc: document, + mirror, + hackCss: false, + cache, + }, + ) as HTMLImageElement; + expect(node?.src).toBe(dataURI); + expect(node?.className).toBe('logo'); + expect(node?.getAttribute('alt')).toBe('Logo'); + expect(node?.getAttribute('sizes')).toBe('32px'); + expect(node?.getAttribute('rrweb-original-srcset')).toBe( + 'http://example.com/image.png 1x, http://example.com/image2x.png 2x', + ); + }); }); describe('rr_width/rr_height', function () {