From 43ff622c1579d91223edc73a4b6dff3621096547 Mon Sep 17 00:00:00 2001 From: wizlor-bfw <282750666+wizlor-bfw@users.noreply.github.com> Date: Thu, 20 Aug 2026 23:07:34 +0000 Subject: [PATCH] fix: keep other img attributes when srcset is backed up during rebuild MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When rebuilding an element that has both a `srcset` attribute and an inlined `rr_dataURL` (used to make locally-recorded images viewable elsewhere), every attribute on the element was being replaced by a repeated `rrweb-original-srcset` backup instead of only `srcset` being backed up. The guard only checked whether the *node* had a `srcset` and `rr_dataURL`, not whether the attribute currently being applied in the loop was actually `srcset` — so `class`, `alt`, `sizes`, `decoding`, `loading`, etc. were all silently dropped on replay. This is the likely cause of highlight/highlight#4853: recordings taken with inlineImages enabled (the default on localhost) of responsive images using `srcset` lose their sizing/styling attributes on replay, producing a distorted "busted" render. Fixes highlight/highlight#4853 --- .../fix-img-srcset-attributes-dropped.md | 5 +++ packages/rrweb-snapshot/src/rebuild.ts | 10 ++--- packages/rrweb-snapshot/test/rebuild.test.ts | 41 +++++++++++++++++++ 3 files changed, 50 insertions(+), 6 deletions(-) create mode 100644 .changeset/fix-img-srcset-attributes-dropped.md 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 () {