diff --git a/.changeset/tiny-rabbits-chew.md b/.changeset/tiny-rabbits-chew.md new file mode 100644 index 0000000000..3f4919f7e3 --- /dev/null +++ b/.changeset/tiny-rabbits-chew.md @@ -0,0 +1,5 @@ +--- +"rrweb": patch +--- + +Make sure MediaInteraction events / mutations are only processed on MediaElements + exit early in addMediaElements if a MediaElement is a blocked element diff --git a/packages/rrweb/src/replay/index.ts b/packages/rrweb/src/replay/index.ts index 5f1101cc23..3d29411ef0 100644 --- a/packages/rrweb/src/replay/index.ts +++ b/packages/rrweb/src/replay/index.ts @@ -1303,14 +1303,17 @@ export class Replayer { return this.debugNodeNotFound(d, d.id); } const mediaEl = target as HTMLMediaElement | RRMediaElement; - const { events } = this.service.state.context; - - this.mediaManager.mediaMutation({ - target: mediaEl, - timeOffset: e.timestamp - events[0].timestamp, - mutation: d, - }); - + // sometimes we receive MediaInteraction events on non-media elements (e.g. DIV) + // only process media mutations on supported media elements to prevent errors during playback + if (this.mediaManager.isSupportedMediaElement(mediaEl)) { + const { events } = this.service.state.context; + + this.mediaManager.mediaMutation({ + target: mediaEl, + timeOffset: e.timestamp - events[0].timestamp, + mutation: d, + }); + } break; } case IncrementalSource.StyleSheetRule: diff --git a/packages/rrweb/src/replay/media/index.ts b/packages/rrweb/src/replay/media/index.ts index ef577cafa0..8b6fe0a81e 100644 --- a/packages/rrweb/src/replay/media/index.ts +++ b/packages/rrweb/src/replay/media/index.ts @@ -210,6 +210,12 @@ export class MediaManager { const target = node as HTMLMediaElement; const serializedNode = mirror.getMeta(target); if (!serializedNode || !('attributes' in serializedNode)) return; + + // don't process if the media element is blocked + const isBlockedMediaElement = + serializedNode.attributes.rr_width || serializedNode.attributes.rr_height; + if (isBlockedMediaElement) return; + const playerIsPaused = this.service.state.matches('paused'); const mediaAttributes = serializedNode.attributes as | mediaAttributes @@ -285,7 +291,9 @@ export class MediaManager { this.syncTargetWithState(target); } - public isSupportedMediaElement(node: Node): node is HTMLMediaElement { + public isSupportedMediaElement( + node: Node | RRMediaElement, + ): node is HTMLMediaElement | RRMediaElement { return ['AUDIO', 'VIDEO'].includes(node.nodeName); }