Skip to content

ignoreAttribute never matches on the attribute-mutation path (tagName case mismatch) #1916

Description

@roggernaut

ignoreAttribute in packages/rrweb-snapshot/src/snapshot.ts compares the tag name against lowercase literals:

export function ignoreAttribute(
  tagName: string,
  name: string,
  _value: unknown,
): boolean {
  return ['video', 'audio'].includes(tagName) && name === 'autoplay';
}

The attribute-mutation handler in packages/rrweb/src/record/mutation.ts calls it with the native target.tagName, which is uppercase for HTML elements:

if (!ignoreAttribute(target.tagName, attributeName, value)) {

'VIDEO' never matches ['video', 'audio'], so autoplay changes made after recording starts get serialized into the event stream. The snapshot path lowercases the tag name before calling ignoreAttribute, so initial snapshots drop the attribute correctly; only live mutations leak through. Replay drives media playback from media-interaction events, so recording the attribute has no use at replay time either.

Affects master and the published rrweb@2.1.1 (its dist calls ignoreAttribute(target.tagName, attributeName) against the same lowercase literals).

Reproduction

import { ignoreAttribute } from 'rrweb-snapshot';

ignoreAttribute('video', 'autoplay', ''); // true  (snapshot path)
ignoreAttribute('VIDEO', 'autoplay', ''); // false (mutation path)

In a page: start recording with a <video> present, call video.setAttribute('autoplay', ''), and the emitted mutation event contains the autoplay attribute.

Fix

Lowercase inside ignoreAttribute so all callers behave the same:

return (
  ['video', 'audio'].includes(toLowerCase(tagName)) && name === 'autoplay'
);

Happy to open a PR with this and a unit test.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions